diff --git a/.asf.yaml b/.asf.yaml index be818fda4d3e..cb957e57fd95 100644 --- a/.asf.yaml +++ b/.asf.yaml @@ -16,7 +16,6 @@ # under the License. # https://cwiki.apache.org/confluence/display/INFRA/git+-+.asf.yaml+features ---- github: description: "Apache CloudStack is an opensource Infrastructure as a Service (IaaS) cloud computing platform" homepage: https://cloudstack.apache.org/ @@ -50,20 +49,34 @@ github: rebase: false collaborators: - - acs-robot - - gpordeus - - hsato03 - - FelipeM525 - - lucas-a-martins - - nicoschmdt - - abh1sar - - rosi-shapeblue - - sudo87 + - ingox + - gp-santos + - erikbocks + - Imvedansh + - Damans227 + - jmsperu + - GaOrtiga + - bhouse-nexthop - protected_branches: ~ + rulesets: + - name: "Default Branch Protection" + type: branch + branches: + includes: + - "~DEFAULT_BRANCH" + excludes: [] + bypass_teams: + - root + restrict_deletion: true + restrict_force_push: true + + copilot_code_review: + enabled: true + review_drafts: true + review_on_push: true notifications: - commits: commits@cloudstack.apache.org - issues: commits@cloudstack.apache.org + commits: commits@cloudstack.apache.org + issues: commits@cloudstack.apache.org pullrequests: commits@cloudstack.apache.org - discussions: users@cloudstack.apache.org + discussions: users@cloudstack.apache.org diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 000000000000..3c632f8ba534 --- /dev/null +++ b/.codespellrc @@ -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. + +[codespell] +ignore-words = .github/linters/codespell.txt +skip = systemvm/agent/noVNC/*,ui/package.json,ui/package-lock.json,ui/public/js/less.min.js,ui/public/locales/*.json,server/src/test/java/org/apache/cloudstack/network/ssl/CertServiceTest.java,test/integration/smoke/test_ssl_offloading.py diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000000..1b06f3ebf53c --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +.github/workflows/*.lock.yml linguist-generated=true merge=ours diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000000..375912667d63 --- /dev/null +++ b/.github/CODEOWNERS @@ -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. + +/plugins/storage/volume/linstor @rp- +/plugins/storage/volume/storpool @slavkap +/plugins/storage/volume/ontap @rajiv-jain-netapp @sandeeplocharla @piyush5netapp @suryag1201 + +.pre-commit-config.yaml @jbampton +/.github/linters/ @jbampton + +/plugins/network-elements/nsx/ @Pearl1594 @nvazquez diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yml similarity index 100% rename from .github/ISSUE_TEMPLATE/feature_request.yaml rename to .github/ISSUE_TEMPLATE/feature_request.yml diff --git a/.github/actions/install-nonoss/action.yml b/.github/actions/install-nonoss/action.yml new file mode 100644 index 000000000000..39a03213c29d --- /dev/null +++ b/.github/actions/install-nonoss/action.yml @@ -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. + +name: 'Install CloudStack Non-OSS' +description: 'Clones and installs the shapeblue/cloudstack-nonoss repository.' + +runs: + using: "composite" + steps: + - name: Install cloudstack-nonoss + shell: bash + run: | + git clone --depth 1 https://github.com/shapeblue/cloudstack-nonoss.git nonoss + cd nonoss + bash -x install-non-oss.sh + cd .. + rm -fr nonoss diff --git a/.github/actions/setup-env/action.yml b/.github/actions/setup-env/action.yml new file mode 100644 index 000000000000..0f8425229242 --- /dev/null +++ b/.github/actions/setup-env/action.yml @@ -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. + +name: 'Setup CloudStack Environment' +description: 'Sets up JDK (with Maven cache), optionally Python, and optionally APT build dependencies for CloudStack.' + +inputs: + java-version: + description: 'The JDK version to use' + required: false + default: '17' + install-python: + description: 'Whether to install Python 3.10' + required: false + default: 'false' + install-apt-deps: + description: 'Whether to install CloudStack APT build dependencies' + required: false + default: 'false' + +runs: + using: "composite" + steps: + - name: Set up JDK ${{ inputs.java-version }} + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + with: + java-version: ${{ inputs.java-version }} + distribution: 'adopt' + architecture: x64 + cache: 'maven' + + - name: Set up Python + if: ${{ inputs.install-python == 'true' }} + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.10' + architecture: x64 + + - name: Install Build Dependencies + if: ${{ inputs.install-apt-deps == 'true' }} + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y git uuid-runtime genisoimage netcat-openbsd ipmitool build-essential libgcrypt20 libgpg-error-dev libgpg-error0 libopenipmi0 libpython3-dev libssl-dev libffi-dev python3-openssl python3-dev python3-setuptools diff --git a/.github/aw/actions-lock.json b/.github/aw/actions-lock.json new file mode 100644 index 000000000000..ea25ffab6b9e --- /dev/null +++ b/.github/aw/actions-lock.json @@ -0,0 +1,56 @@ +{ + "entries": { + "actions/github-script@v9.0.0": { + "repo": "actions/github-script", + "version": "v9.0.0", + "sha": "3a2844b7e9c422d3c10d287c895573f7108da1b3" + }, + "github/gh-aw-actions/setup@v0.76.1": { + "repo": "github/gh-aw-actions/setup", + "version": "v0.76.1", + "sha": "46d564922b082d0db93244972e8005ea6904ee5f" + } + }, + "containers": { + "ghcr.io/github/gh-aw-firewall/agent:0.18.0": { + "image": "ghcr.io/github/gh-aw-firewall/agent:0.18.0", + "digest": "sha256:ab84dfc7f5998cb8cd0c596526dd573b7e7d06c6a740266a1e6df879fa16c866", + "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.18.0@sha256:ab84dfc7f5998cb8cd0c596526dd573b7e7d06c6a740266a1e6df879fa16c866" + }, + "ghcr.io/github/gh-aw-firewall/agent:0.25.55": { + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.55", + "digest": "sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731", + "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.55@sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731" + }, + "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55": { + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55", + "digest": "sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3", + "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55@sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3" + }, + "ghcr.io/github/gh-aw-firewall/squid:0.18.0": { + "image": "ghcr.io/github/gh-aw-firewall/squid:0.18.0", + "digest": "sha256:82a5d062a5612a57a43a171a5b79ddbb690a86a8ddda02339cc1675131ae9f8b", + "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.18.0@sha256:82a5d062a5612a57a43a171a5b79ddbb690a86a8ddda02339cc1675131ae9f8b" + }, + "ghcr.io/github/gh-aw-firewall/squid:0.25.55": { + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.55", + "digest": "sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca", + "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.55@sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca" + }, + "ghcr.io/github/gh-aw-mcpg:v0.1.4": { + "image": "ghcr.io/github/gh-aw-mcpg:v0.1.4", + "digest": "sha256:0acf25aa1d409f9c73be9e39ac84f4bd4b90d8bfa1db4dc6d7f47d38ccd58914", + "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.1.4@sha256:0acf25aa1d409f9c73be9e39ac84f4bd4b90d8bfa1db4dc6d7f47d38ccd58914" + }, + "ghcr.io/github/gh-aw-mcpg:v0.3.19": { + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.19", + "digest": "sha256:a6c890d7c24d7190c9ef97b9c954cc4cffaae6b01c371ced1f959f1370b1f68f", + "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.19@sha256:a6c890d7c24d7190c9ef97b9c954cc4cffaae6b01c371ced1f959f1370b1f68f" + }, + "ghcr.io/github/github-mcp-server:v0.30.3": { + "image": "ghcr.io/github/github-mcp-server:v0.30.3", + "digest": "sha256:a2b5fb79b1cee851bfc3532dfe480c3dc5736974ca9d93a7a9f68e52ce4b62a0", + "pinned_image": "ghcr.io/github/github-mcp-server:v0.30.3@sha256:a2b5fb79b1cee851bfc3532dfe480c3dc5736974ca9d93a7a9f68e52ce4b62a0" + } + } +} diff --git a/.github/aw/imports/.gitattributes b/.github/aw/imports/.gitattributes new file mode 100644 index 000000000000..f0516fad90e4 --- /dev/null +++ b/.github/aw/imports/.gitattributes @@ -0,0 +1,5 @@ +# Mark all cached import files as generated +* linguist-generated=true + +# Use 'ours' merge strategy to keep local cached versions +* merge=ours diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..6ffb926f6fa0 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,43 @@ +# 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. + +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + open-pull-requests-limit: 2 + schedule: + interval: "weekly" + groups: + github-actions-dependencies: + patterns: + - "*" + ignore: + - dependency-name: "github/gh-aw-actions/**" # Managed by gh aw compile. Version-locked to the gh-aw compiler; do not bump. + cooldown: + default-days: 7 + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "daily" + cooldown: + default-days: 7 diff --git a/.github/linters/.markdown-lint.yml b/.github/linters/.markdown-lint.yml index 531c26ace444..6a8a26f48497 100644 --- a/.github/linters/.markdown-lint.yml +++ b/.github/linters/.markdown-lint.yml @@ -18,9 +18,6 @@ # MD001/heading-increment Heading levels should only increment by one level at a time MD001: false -# MD003/heading-style Heading style -MD003: false - # MD004/ul-style Unordered list style MD004: false diff --git a/.github/linters/.yamllint.yml b/.github/linters/.yamllint.yml new file mode 100644 index 000000000000..97b66848696a --- /dev/null +++ b/.github/linters/.yamllint.yml @@ -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. +--- +extends: default + +rules: + line-length: + max: 400 # Very forgiving for GitHub Actions and infrastructure files + indentation: disable # Disable indentation checking for existing files + comments: disable # Disable comment formatting checks + braces: disable + brackets: disable # Disable bracket spacing checks + colons: + max-spaces-after: -1 # Allow any number of spaces after colon + max-spaces-before: 0 + document-start: disable # Many files don't have --- + truthy: + allowed-values: ['true', 'false', 'on', 'off', 'yes', 'no'] diff --git a/.github/linters/codespell.txt b/.github/linters/codespell.txt index 37b3e6de1cbe..67cbeaa7cbb3 100644 --- a/.github/linters/codespell.txt +++ b/.github/linters/codespell.txt @@ -4,6 +4,7 @@ acount actuall acuiring acumulate +addin addreess addtion adminstrator @@ -12,10 +13,8 @@ afrer afterall againt ags -aktive algoritm allo -alloacate allocted alocation alogrithm @@ -65,6 +64,7 @@ bject boardcast bootstraper bu +callin cant capabilites capablity @@ -73,6 +73,7 @@ carrefully cavaet chaing checkd +checkin childs choosen chould @@ -93,7 +94,6 @@ confg configruation configuable conneciton -connexion constrait constraits containg @@ -101,9 +101,7 @@ contex continuesly contro controler -controles controll -convienient convinience coputer correcponding @@ -158,13 +156,13 @@ differnet differnt direcotry directroy -disale disbale discrepency disover dissapper dissassociated divice +dockin doesn' doesnot doesnt @@ -175,7 +173,6 @@ eanbled earch ect elemnt -eles elments emmited enble @@ -183,29 +180,23 @@ encryted enebled enmpty entires -enviornment environmnet equivalant erro erronous -everthing everytime -excetion -excption excute execept execption +exects execut executeable exeeded exisitng exisits -existin existsing -exitting expcted expection -explaination explicitely faield faild @@ -218,7 +209,6 @@ fillled findout fisrt fo -folowing fowarding frist fro @@ -237,6 +227,7 @@ hanling happend hasing hasnt +havin hda hostanme hould @@ -256,20 +247,14 @@ implmeneted implmentation incase includeing -incosistency indecates -indien infor informations informaton -infrastrcuture ingore -inital initalize initator -initilization inspite -instace instal instnace intefaces @@ -287,12 +272,8 @@ ist klunky lable leve -lief limite -linke listner -lokal -lokales maintainence maintenace maintenence @@ -301,7 +282,6 @@ mambers manaully manuel maxium -mehtod mergable mesage messge @@ -311,7 +291,6 @@ minumum mis modifers mor -mot mulitply multipl multple @@ -325,7 +304,7 @@ nin nodel nome noone -nowe +notin numbe numer occured @@ -378,6 +357,7 @@ propogate provison psudo pyhsical +re-use readabilty readd reccuring @@ -392,12 +372,9 @@ remaning remore remvoing renabling -repeatly reponse reqest reqiured -requieres -requried reserv reserverd reseted @@ -414,17 +391,15 @@ retriving retrun retuned returing -re-use rever rocessor +roperty runing runnign sate scalled -scipt scirpt scrip -seconadry seconday seesion sepcified @@ -437,12 +412,10 @@ settig sevices shoul shoule -sie signle simplier singature skiping -snaphsot snpashot specied specifed @@ -453,7 +426,6 @@ standy statics stickyness stil -stip storeage strat streched @@ -462,7 +434,6 @@ succesfull successfull suceessful suces -sucessfully suiteable suppots suppport @@ -495,7 +466,6 @@ uncompressible uneccessarily unexepected unexpect -unknow unkonw unkown unneccessary @@ -503,14 +473,12 @@ unparseable unrecoginized unsupport unxpected -updat uptodate usera usign usin utlization vaidate -valiate valule valus varibles @@ -519,8 +487,6 @@ verfying verifing virutal visable -wakup wil wit -wll wth diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 06f9f424c7f3..9c8ca10f85e9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,40 +16,31 @@ # under the License. name: Build - -on: [push, pull_request] - +on: + push: + branches: + - main + - 4.22 + - 4.20 + pull_request: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} permissions: contents: read - jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 - - - name: Set up JDK 17 - uses: actions/setup-java@v4 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: - distribution: 'temurin' - java-version: '17' - cache: 'maven' + persist-credentials: false - - name: Set up Python - uses: actions/setup-python@v5 + - name: Setup Environment + uses: ./.github/actions/setup-env with: - python-version: '3.10' - architecture: 'x64' - - - name: Install Build Dependencies - run: | - sudo apt-get update - sudo apt-get install -y git uuid-runtime genisoimage netcat ipmitool build-essential libgcrypt20 libgpg-error-dev libgpg-error0 libopenipmi0 ipmitool libpython3-dev libssl-dev libffi-dev python3-openssl python3-dev python3-setuptools - + install-python: 'true' + install-apt-deps: 'true' - name: Env details run: | uname -a @@ -60,9 +51,8 @@ jobs: free -m nproc git status - + - name: Install Non-OSS + uses: ./.github/actions/install-nonoss - name: Noredist Build run: | - git clone https://github.com/shapeblue/cloudstack-nonoss.git nonoss && cd nonoss && bash -x install-non-oss.sh && cd .. - rm -fr nonoss mvn -B -P developer,systemvm -Dsimulator -Dnoredist clean install -T$(nproc) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 471e56e3f07b..7f6645f4af4d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,21 +16,60 @@ # under the License. name: Simulator CI - -on: [push, pull_request] - +on: + push: + branches: + - main + - 4.22 + - 4.20 + pull_request: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} permissions: contents: read - jobs: build: if: github.repository == 'apache/cloudstack' - runs-on: ubuntu-22.04 - + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + fetch-depth: 0 + persist-credentials: false + - name: Setup Environment + uses: ./.github/actions/setup-env + with: + install-python: 'true' + install-apt-deps: 'true' + - name: Env details + run: | + uname -a + whoami + javac -version + mvn -v + python3 --version + free -m + nproc + git status + ipmitool -V + - name: Build with Maven + run: | + mvn -B -P developer,systemvm -Dsimulator clean install -DskipTests=true -T$(nproc) + - name: Archive artifacts + run: | + mkdir -p /tmp/artifacts + tar -czf /tmp/artifacts/targets.tar.gz $(find . -name "target" -type d) tools/marvin/dist engine/schema/dist utils/conf + tar -czf /tmp/artifacts/m2-cloudstack.tar.gz -C ~/.m2/repository org/apache/cloudstack + - name: Upload artifacts + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: build-artifacts + path: /tmp/artifacts/ + test: + needs: build + if: github.repository == 'apache/cloudstack' + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: @@ -146,6 +185,7 @@ jobs: smoke/test_vm_snapshot_kvm smoke/test_vm_snapshots smoke/test_volumes + smoke/test_vpc_conserve_mode smoke/test_vpc_ipv6 smoke/test_vpc_redundant smoke/test_vpc_router_nics @@ -214,78 +254,70 @@ jobs: smoke/test_list_service_offerings smoke/test_list_storage_pools smoke/test_list_volumes"] - steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 - - - name: Set up JDK 17 - uses: actions/setup-java@v4 + persist-credentials: false + - name: Setup Environment + uses: ./.github/actions/setup-env with: - distribution: 'temurin' - java-version: '17' - cache: 'maven' - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.10' - architecture: 'x64' - - - name: Install Build Dependencies + install-python: 'true' + install-apt-deps: 'true' + - name: Setup IPMI Tool for CloudStack run: | - sudo apt-get update - sudo apt-get install -y git uuid-runtime genisoimage netcat ipmitool build-essential libgcrypt20 libgpg-error-dev libgpg-error0 libopenipmi0 ipmitool libpython3-dev libssl-dev libffi-dev python3-openssl python3-dev python3-setuptools + # Create cloudstack-common directory if it doesn't exist + sudo mkdir -p /usr/share/cloudstack-common + + # Copy ipmitool to cloudstack-common directory if it doesn't exist + if [ ! -f /usr/share/cloudstack-common/ipmitool ]; then + sudo cp /usr/bin/ipmitool /usr/share/cloudstack-common/ipmitool + sudo chmod 755 /usr/share/cloudstack-common/ipmitool + fi + # Create ipmitool-C3 wrapper script + sudo tee /usr/bin/ipmitool > /dev/null << 'EOF' + #!/bin/bash + /usr/share/cloudstack-common/ipmitool -C3 $@ + EOF + sudo chmod 755 /usr/bin/ipmitool - name: Install Python dependencies run: | 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: | wget https://github.com/jacoco/jacoco/releases/download/v0.8.10/jacoco-0.8.10.zip unzip jacoco-0.8.10.zip -d jacoco - - - name: Env details - run: | - uname -a - whoami - javac -version - mvn -v - python3 --version - free -m - nproc - git status - ipmitool -V - - name: Setup MySQL Server run: | # https://github.com/actions/runner-images/blob/main/images/linux/Ubuntu2004-Readme.md#mysql sudo apt-get install -y mysql-server sudo systemctl start mysql - sudo mysql -uroot -proot -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY ''; FLUSH PRIVILEGES;" + sudo mysql -uroot -proot -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY ''; FLUSH PRIVILEGES;" sudo systemctl restart mysql sudo mysql -uroot -e "SELECT VERSION();" - - - name: Build with Maven + - name: Download artifacts + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: build-artifacts + path: /tmp/artifacts/ + - name: Extract artifacts run: | - mvn -B -P developer,systemvm -Dsimulator clean install -DskipTests=true -T$(nproc) - + tar -xzf /tmp/artifacts/targets.tar.gz + mkdir -p ~/.m2/repository + tar -xzf /tmp/artifacts/m2-cloudstack.tar.gz -C ~/.m2/repository - name: Setup Simulator Prerequisites run: | sudo python3 -m pip install --upgrade netaddr mysql-connector-python - python3 -m pip install --user --upgrade tools/marvin/dist/Marvin-*.tar.gz + python3 -m pip install --user --upgrade tools/marvin/dist/[mM]arvin-*.tar.gz mvn -q -Pdeveloper -pl developer -Ddeploydb mvn -q -Pdeveloper -pl developer -Ddeploydb-simulator - - name: Generate jacoco-coverage.sh run: | echo "java -jar jacoco/lib/jacococli.jar report jacoco-it.exec \\" > jacoco-report.sh find . | grep "target/classes" | sed 's/\/classes\//\/classes /g' | awk '{print "--classfiles", $1, "\\"}' | sort |uniq >> jacoco-report.sh find . | grep "src/main/java" | sed 's/\/java\//\/java /g' | awk '{print "--sourcefiles", $1, "\\"}' | sort | uniq >> jacoco-report.sh echo "--xml jacoco-coverage.xml" >> jacoco-report.sh - - name: Start CloudStack Management Server with Simulator run: | export MAVEN_OPTS="-Xmx4096m -XX:MaxMetaspaceSize=800m -Djava.security.egd=file:/dev/urandom -javaagent:jacoco/lib/jacocoagent.jar=address=*,port=36320,output=tcpserver --add-opens=java.base/java.lang=ALL-UNNAMED --add-exports=java.base/sun.security.x509=ALL-UNNAMED --add-opens=java.base/jdk.internal.reflect=ALL-UNNAMED" @@ -296,7 +328,6 @@ jobs: set -e echo -e "\nStarting Advanced Zone DataCenter deployment" python3 tools/marvin/marvin/deployDataCenter.py -i setup/dev/advdualzone.cfg 2>&1 || true - - name: Run Integration Tests with Simulator run: | mkdir -p integration-test-results/smoke/misc @@ -316,13 +347,12 @@ jobs: bash jacoco-report.sh mvn -Dsimulator -pl client jetty:stop 2>&1 find /tmp//MarvinLogs -type f -exec echo -e "Printing marvin logs {} :\n" \; -exec cat {} \; - - name: Integration Tests Result run: | echo -e "Simulator CI Test Results: (only failures listed)\n" python3 ./tools/marvin/xunit-reader.py integration-test-results/ - - uses: codecov/codecov-action@v4 + - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: files: jacoco-coverage.xml fail_ci_if_error: true diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml deleted file mode 100644 index c77783746ca1..000000000000 --- a/.github/workflows/codecov.yml +++ /dev/null @@ -1,59 +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. - -name: Coverage Check - -on: [pull_request, push] - -permissions: - contents: read - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - build: - if: github.repository == 'apache/cloudstack' - name: codecov - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: '17' - cache: 'maven' - - - name: Build CloudStack with Quality Checks - run: | - git clone https://github.com/shapeblue/cloudstack-nonoss.git nonoss - cd nonoss && bash -x install-non-oss.sh && cd .. - mvn -P quality -Dsimulator -Dnoredist clean install -T$(nproc) - - - uses: codecov/codecov-action@v4 - with: - files: ./client/target/site/jacoco-aggregate/jacoco.xml - fail_ci_if_error: true - flags: unittests - verbose: true - name: codecov - token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 000000000000..f6bc65ad4b5b --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -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. + +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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + - name: Initialize CodeQL + uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + with: + languages: ${{ matrix.language }} + - name: Autobuild + uses: github/codeql-action/autobuild@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + with: + category: "Security" diff --git a/.github/workflows/daily-issue-triage.lock.yml b/.github/workflows/daily-issue-triage.lock.yml new file mode 100644 index 000000000000..bd07aeefd811 --- /dev/null +++ b/.github/workflows/daily-issue-triage.lock.yml @@ -0,0 +1,1400 @@ +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"919fb17c7928e5e96d9c0a2854670a42f9c5f6cfc2059b46009bb3c23640d0ca","compiler_version":"v0.76.1","strict":true,"agent_id":"copilot"} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"46d564922b082d0db93244972e8005ea6904ee5f","version":"v0.76.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.55","digest":"sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.25.55@sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55","digest":"sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55@sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.55","digest":"sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.25.55@sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.19","digest":"sha256:a6c890d7c24d7190c9ef97b9c954cc4cffaae6b01c371ced1f959f1370b1f68f","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.19@sha256:a6c890d7c24d7190c9ef97b9c954cc4cffaae6b01c371ced1f959f1370b1f68f"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4","digest":"sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4"},{"image":"node:lts-alpine","digest":"sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14","pinned_image":"node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14"}]} +# ___ _ _ +# / _ \ | | (_) +# | |_| | __ _ ___ _ __ | |_ _ ___ +# | _ |/ _` |/ _ \ '_ \| __| |/ __| +# | | | | (_| | __/ | | | |_| | (__ +# \_| |_/\__, |\___|_| |_|\__|_|\___| +# __/ | +# _ _ |___/ +# | | | | / _| | +# | | | | ___ _ __ _ __| |_| | _____ ____ +# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| +# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ +# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ +# +# This file was automatically generated by gh-aw (v0.76.1). DO NOT EDIT. +# +# To update this file, edit githubnext/agentics/workflows/daily-issue-triage.md@d7c1dc4b72b00607a67caaffdcc216cb64379cf9 and run: +# gh aw compile +# Not all edits will cause changes to this file. +# +# For more information: https://github.github.com/gh-aw/introduction/overview/ +# +# Scheduled daily triage that processes untriaged CloudStack issues in batches. +# Detects duplicates, filters spam, and assigns CloudStack-specific labels +# (type:*, component:*, Severity:*, status:*), then posts a structured triage report. +# +# Source: githubnext/agentics/workflows/daily-issue-triage.md@d7c1dc4b72b00607a67caaffdcc216cb64379cf9 +# +# Secrets used: +# - COPILOT_GITHUB_TOKEN +# - GH_AW_GITHUB_MCP_SERVER_TOKEN +# - GH_AW_GITHUB_TOKEN +# - GITHUB_TOKEN +# +# Custom actions used: +# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 +# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 +# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 +# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 +# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 +# - github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8 +# +# Container images used: +# - ghcr.io/github/gh-aw-firewall/agent:0.25.55@sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731 +# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55@sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3 +# - ghcr.io/github/gh-aw-firewall/squid:0.25.55@sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca +# - ghcr.io/github/gh-aw-mcpg:v0.3.19@sha256:a6c890d7c24d7190c9ef97b9c954cc4cffaae6b01c371ced1f959f1370b1f68f +# - ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4 +# - node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14 + +name: "Daily Issue Triage" +on: + schedule: + - cron: "26 13 * * 1-5" + # Friendly format: daily around 14:00 on weekdays (scattered) + workflow_dispatch: + inputs: + aw_context: + default: "" + description: "Agent caller context (used internally by Agentic Workflows)." + required: false + type: string + +permissions: {} + +concurrency: + group: "gh-aw-${{ github.workflow }}" + +run-name: "Daily Issue Triage" + +jobs: + activation: + runs-on: ubuntu-slim + permissions: + actions: read + contents: read + outputs: + comment_id: "" + comment_repo: "" + engine_id: ${{ steps.generate_aw_info.outputs.engine_id }} + lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }} + model: ${{ steps.generate_aw_info.outputs.model }} + secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} + setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} + setup-span-id: ${{ steps.setup.outputs.span-id }} + setup-trace-id: ${{ steps.setup.outputs.trace-id }} + stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }} + steps: + - name: Setup Scripts + id: setup + uses: github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Daily Issue Triage" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-issue-triage.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.52" + GH_AW_INFO_AWF_VERSION: "v0.25.55" + GH_AW_INFO_BODY_MODIFIED: "false" + GH_AW_INFO_ENGINE_ID: "copilot" + - name: Generate agentic run info + id: generate_aw_info + env: + GH_AW_INFO_ENGINE_ID: "copilot" + GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" + GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.6' }} + GH_AW_INFO_VERSION: "1.0.52" + GH_AW_INFO_AGENT_VERSION: "1.0.52" + GH_AW_INFO_CLI_VERSION: "v0.76.1" + GH_AW_INFO_WORKFLOW_NAME: "Daily Issue Triage" + GH_AW_INFO_EXPERIMENTAL: "false" + GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" + GH_AW_INFO_STAGED: "false" + GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]' + GH_AW_INFO_FIREWALL_ENABLED: "true" + GH_AW_INFO_AWF_VERSION: "v0.25.55" + GH_AW_INFO_AWMG_VERSION: "" + GH_AW_INFO_FIREWALL_TYPE: "squid" + GH_AW_INFO_FRONTMATTER_SOURCE: "githubnext/agentics/workflows/daily-issue-triage.md@d7c1dc4b72b00607a67caaffdcc216cb64379cf9" + GH_AW_INFO_BODY_MODIFIED: "false" + GH_AW_COMPILED_STRICT: "true" + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs'); + await main(core, context); + - name: Validate COPILOT_GITHUB_TOKEN secret + id: validate-secret + run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_multi_secret.sh" COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default + env: + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + - name: Checkout .github and .agents folders + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + sparse-checkout: | + .github + .agents + .antigravity + .claude + .codex + .crush + .gemini + .opencode + .pi + sparse-checkout-cone-mode: true + fetch-depth: 1 + - name: Save agent config folders for base branch restoration + env: + GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi" + GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc" + # poutine:ignore untrusted_checkout_exec + run: bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh" + - name: Check workflow lock file + id: check-lock-file + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_WORKFLOW_FILE: "daily-issue-triage.lock.yml" + GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}" + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/check_workflow_timestamp_api.cjs'); + await main(); + - name: Check compile-agentic version + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_COMPILED_VERSION: "v0.76.1" + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/check_version_updates.cjs'); + await main(); + - name: Create prompt with built-in context + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl + GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + # poutine:ignore untrusted_checkout_exec + run: | + bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" + { + cat << 'GH_AW_PROMPT_7c51e8f15cc7af75_EOF' + + GH_AW_PROMPT_7c51e8f15cc7af75_EOF + cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" + cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" + cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" + cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" + cat << 'GH_AW_PROMPT_7c51e8f15cc7af75_EOF' + + Tools: add_comment(max:10), add_labels(max:10), missing_tool, missing_data, noop + + GH_AW_PROMPT_7c51e8f15cc7af75_EOF + cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" + cat << 'GH_AW_PROMPT_7c51e8f15cc7af75_EOF' + + The following GitHub context information is available for this workflow: + {{#if github.actor}} + - **actor**: __GH_AW_GITHUB_ACTOR__ + {{/if}} + {{#if github.repository}} + - **repository**: __GH_AW_GITHUB_REPOSITORY__ + {{/if}} + {{#if github.workspace}} + - **workspace**: __GH_AW_GITHUB_WORKSPACE__ + {{/if}} + {{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}} + - **issue-number**: #__GH_AW_EXPR_802A9F6A__ + {{/if}} + {{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}} + - **discussion-number**: #__GH_AW_EXPR_1A3A194A__ + {{/if}} + {{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}} + - **pull-request-number**: #__GH_AW_EXPR_463A214A__ + {{/if}} + {{#if github.event.comment.id || github.aw.context.comment_id}} + - **comment-id**: __GH_AW_EXPR_FF1D34CE__ + {{/if}} + {{#if github.run_id}} + - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ + {{/if}} + + + GH_AW_PROMPT_7c51e8f15cc7af75_EOF + cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" + cat << 'GH_AW_PROMPT_7c51e8f15cc7af75_EOF' + + {{#runtime-import .github/workflows/daily-issue-triage.md}} + GH_AW_PROMPT_7c51e8f15cc7af75_EOF + } > "$GH_AW_PROMPT" + - name: Interpolate variables and render templates + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_ENGINE_ID: "copilot" + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/interpolate_prompt.cjs'); + await main(); + - name: Substitute placeholders + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools' + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + + const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs'); + + // Call the substitution function + return await substitutePlaceholders({ + file: process.env.GH_AW_PROMPT, + substitutions: { + GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A, + GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A, + GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A, + GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE, + GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, + GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, + GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, + GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, + GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST + } + }); + - name: Validate prompt placeholders + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + # poutine:ignore untrusted_checkout_exec + run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh" + - name: Print prompt + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + # poutine:ignore untrusted_checkout_exec + run: bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh" + - name: Upload activation artifact + if: success() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: activation + include-hidden-files: true + path: | + /tmp/gh-aw/aw_info.json + /tmp/gh-aw/aw-prompts/prompt.txt + /tmp/gh-aw/aw-prompts/prompt-template.txt + /tmp/gh-aw/aw-prompts/prompt-import-tree.json + /tmp/gh-aw/github_rate_limits.jsonl + /tmp/gh-aw/base + /tmp/gh-aw/.github/agents + /tmp/gh-aw/.github/skills + if-no-files-found: ignore + retention-days: 1 + + agent: + needs: activation + runs-on: ubuntu-latest + permissions: read-all + concurrency: + group: "gh-aw-copilot-${{ github.workflow }}" + env: + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + GH_AW_ASSETS_ALLOWED_EXTS: "" + GH_AW_ASSETS_BRANCH: "" + GH_AW_ASSETS_MAX_SIZE_KB: 0 + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + GH_AW_WORKFLOW_ID_SANITIZED: dailyissuetriage + outputs: + agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} + checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} + effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }} + effective_tokens_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.effective_tokens_rate_limit_error || 'false' }} + has_patch: ${{ steps.collect_output.outputs.has_patch }} + inference_access_error: ${{ steps.detect-agent-errors.outputs.inference_access_error || 'false' }} + mcp_policy_error: ${{ steps.detect-agent-errors.outputs.mcp_policy_error || 'false' }} + model: ${{ needs.activation.outputs.model }} + model_not_supported_error: ${{ steps.detect-agent-errors.outputs.model_not_supported_error || 'false' }} + output: ${{ steps.collect_output.outputs.output }} + output_types: ${{ steps.collect_output.outputs.output_types }} + setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} + setup-span-id: ${{ steps.setup.outputs.span-id }} + setup-trace-id: ${{ steps.setup.outputs.trace-id }} + steps: + - name: Setup Scripts + id: setup + uses: github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Daily Issue Triage" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-issue-triage.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.52" + GH_AW_INFO_AWF_VERSION: "v0.25.55" + GH_AW_INFO_BODY_MODIFIED: "false" + GH_AW_INFO_ENGINE_ID: "copilot" + - name: Set runtime paths + id: set-runtime-paths + run: | + { + echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl" + echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" + echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json" + } >> "$GITHUB_OUTPUT" + - name: Checkout repository + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + - name: Create gh-aw temp directory + run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh" + - name: Configure gh CLI for GitHub Enterprise + run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh" + env: + GH_TOKEN: ${{ github.token }} + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + GITHUB_TOKEN: ${{ github.token }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Checkout PR branch + id: checkout-pr + if: | + github.event.pull_request || github.event.issue.pull_request + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + with: + github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs'); + await main(); + - name: Install GitHub Copilot CLI + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.52 + env: + GH_HOST: github.com + - name: Install AWF binary + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.55 + - name: Parse integrity filter lists + id: parse-guard-vars + env: + GH_AW_BLOCKED_USERS_VAR: ${{ vars.GH_AW_GITHUB_BLOCKED_USERS || '' }} + GH_AW_TRUSTED_USERS_VAR: ${{ vars.GH_AW_GITHUB_TRUSTED_USERS || '' }} + GH_AW_APPROVAL_LABELS_VAR: ${{ vars.GH_AW_GITHUB_APPROVAL_LABELS || '' }} + run: bash "${RUNNER_TEMP}/gh-aw/actions/parse_guard_list.sh" + - name: Download activation artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: activation + path: /tmp/gh-aw + - name: Restore agent config folders from base branch + if: steps.checkout-pr.outcome == 'success' + env: + GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi" + GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc" + run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh" + - name: Restore inline sub-agents from activation artifact + env: + GH_AW_SUB_AGENT_DIR: ".github/agents" + GH_AW_SUB_AGENT_EXT: ".agent.md" + run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh" + - name: Restore inline skills from activation artifact + env: + GH_AW_SKILL_DIR: ".github/skills" + run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh" + - name: Download container images + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.55@sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55@sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3 ghcr.io/github/gh-aw-firewall/squid:0.25.55@sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca ghcr.io/github/gh-aw-mcpg:v0.3.19@sha256:a6c890d7c24d7190c9ef97b9c954cc4cffaae6b01c371ced1f959f1370b1f68f ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4 node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14 + - name: Generate Safe Outputs Config + run: | + mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" + mkdir -p /tmp/gh-aw/safeoutputs + mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_09fd9551c3cd7278_EOF' + {"add_comment":{"max":10,"target":"*"},"add_labels":{"max":10,"target":"*"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}} + GH_AW_SAFE_OUTPUTS_CONFIG_09fd9551c3cd7278_EOF + - name: Generate Safe Outputs Tools + env: + GH_AW_TOOLS_META_JSON: | + { + "description_suffixes": { + "add_comment": " CONSTRAINTS: Maximum 10 comment(s) can be added. Target: *. Supports reply_to_id for discussion threading.", + "add_labels": " CONSTRAINTS: Maximum 10 label(s) can be added. Target: *." + }, + "repo_params": {}, + "dynamic_tools": [] + } + GH_AW_VALIDATION_JSON: | + { + "add_comment": { + "defaultMax": 1, + "fields": { + "body": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "item_number": { + "issueOrPRNumber": true + }, + "reply_to_id": { + "type": "string", + "maxLength": 256 + }, + "repo": { + "type": "string", + "maxLength": 256 + } + } + }, + "add_labels": { + "defaultMax": 5, + "fields": { + "item_number": { + "issueNumberOrTemporaryId": true + }, + "labels": { + "required": true, + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 + }, + "repo": { + "type": "string", + "maxLength": 256 + } + } + }, + "missing_data": { + "defaultMax": 20, + "fields": { + "alternatives": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "context": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "data_type": { + "type": "string", + "sanitize": true, + "maxLength": 128 + }, + "reason": { + "type": "string", + "sanitize": true, + "maxLength": 256 + } + } + }, + "missing_tool": { + "defaultMax": 20, + "fields": { + "alternatives": { + "type": "string", + "sanitize": true, + "maxLength": 512 + }, + "reason": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "tool": { + "type": "string", + "sanitize": true, + "maxLength": 128 + } + } + }, + "noop": { + "defaultMax": 1, + "fields": { + "message": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + } + } + }, + "report_incomplete": { + "defaultMax": 5, + "fields": { + "details": { + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "reason": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 1024 + } + } + } + } + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_safe_outputs_tools.cjs'); + await main(); + - name: Generate Safe Outputs MCP Server Config + id: safe-outputs-config + run: | + # Generate a secure random API key (360 bits of entropy, 40+ chars) + # Mask immediately to prevent timing vulnerabilities + API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${API_KEY}" + + PORT=3001 + + # Set outputs for next steps + { + echo "safe_outputs_api_key=${API_KEY}" + echo "safe_outputs_port=${PORT}" + } >> "$GITHUB_OUTPUT" + + echo "Safe Outputs MCP server will run on port ${PORT}" + + - name: Start Safe Outputs MCP HTTP Server + id: safe-outputs-start + env: + DEBUG: '*' + GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }} + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/tools.json + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/config.json + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + run: | + # Environment variables are set above to prevent template injection + export DEBUG + export GH_AW_SAFE_OUTPUTS + export GH_AW_SAFE_OUTPUTS_PORT + export GH_AW_SAFE_OUTPUTS_API_KEY + export GH_AW_SAFE_OUTPUTS_TOOLS_PATH + export GH_AW_SAFE_OUTPUTS_CONFIG_PATH + export GH_AW_MCP_LOG_DIR + + bash "${RUNNER_TEMP}/gh-aw/actions/start_safe_outputs_server.sh" + + - name: Start MCP Gateway + id: start-mcp-gateway + env: + GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + run: | + set -eo pipefail + mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config" + + # Export gateway environment variables for MCP config and gateway script + export MCP_GATEWAY_PORT="8080" + export MCP_GATEWAY_DOMAIN="host.docker.internal" + export MCP_GATEWAY_HOST_DOMAIN="localhost" + MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${MCP_GATEWAY_API_KEY}" + export MCP_GATEWAY_API_KEY + export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" + mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" + export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" + export DEBUG="*" + + export GH_AW_ENGINE="copilot" + MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0') + MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0') + case "${DOCKER_HOST:-}" in + unix://* ) DOCKER_SOCK_PATH="${DOCKER_HOST#unix://}" ;; + /* ) DOCKER_SOCK_PATH="$DOCKER_HOST" ;; + * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;; + esac + DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0') + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.3.19' + + mkdir -p /home/runner/.copilot + GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) + cat << GH_AW_MCP_CONFIG_37cac1d5ee0c175c_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + { + "mcpServers": { + "github": { + "type": "stdio", + "container": "ghcr.io/github/github-mcp-server:v1.0.4", + "env": { + "GITHUB_HOST": "\${GITHUB_SERVER_URL}", + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", + "GITHUB_READ_ONLY": "1", + "GITHUB_TOOLSETS": "issues,labels" + }, + "guard-policies": { + "allow-only": { + "approval-labels": ${{ steps.parse-guard-vars.outputs.approval_labels }}, + "blocked-users": ${{ steps.parse-guard-vars.outputs.blocked_users }}, + "min-integrity": "none", + "repos": "all", + "trusted-users": ${{ steps.parse-guard-vars.outputs.trusted_users }} + } + } + }, + "safeoutputs": { + "type": "http", + "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", + "headers": { + "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" + }, + "guard-policies": { + "write-sink": { + "accept": [ + "*" + ] + } + } + } + }, + "gateway": { + "port": $MCP_GATEWAY_PORT, + "domain": "${MCP_GATEWAY_DOMAIN}", + "apiKey": "${MCP_GATEWAY_API_KEY}", + "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" + } + } + GH_AW_MCP_CONFIG_37cac1d5ee0c175c_EOF + - name: Mount MCP servers as CLIs + id: mount-mcp-clis + continue-on-error: true + env: + MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} + MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }} + MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs'); + await main(); + - name: Clean credentials + continue-on-error: true + run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh" + - name: Audit pre-agent workspace + id: pre_agent_audit + continue-on-error: true + run: bash "${RUNNER_TEMP}/gh-aw/actions/audit_pre_agent_workspace.sh" + - name: Execute GitHub Copilot CLI + id: agentic_execution + # Copilot CLI tool arguments (sorted): + timeout-minutes: 60 + run: | + set -o pipefail + printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt + touch /tmp/gh-aw/agent-step-summary.md + GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) + export GH_AW_NODE_BIN + export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" + (umask 177 && touch /tmp/gh-aw/agent-stdio.log) + printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.55/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.com","crl.geotrust.com","crl.globalsign.com","crl.identrust.com","crl.sectigo.com","crl.thawte.com","crl.usertrust.com","crl.verisign.com","crl3.digicert.com","crl4.digicert.com","crls.ssl.com","github.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.com","ocsp.digicert.com","ocsp.geotrust.com","ocsp.globalsign.com","ocsp.identrust.com","ocsp.sectigo.com","ocsp.ssl.com","ocsp.thawte.com","ocsp.usertrust.com","ocsp.verisign.com","packagecloud.io","packages.cloud.google.com","packages.microsoft.com","ppa.launchpad.net","raw.githubusercontent.com","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","telemetry.enterprise.githubcopilot.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000,"models":{"agent":["sonnet-6x","gpt-5.4","gpt-5.3","gemini-pro","any"],"antigravity":["copilot/antigravity*","google/antigravity*","gemini/antigravity*"],"any":["copilot/*","anthropic/*","openai/*","google/*","gemini/*"],"claude":["agent"],"codex":["agent"],"coding":["copilot/gpt-5*codex*","openai/gpt-5*codex*","gpt-5-codex"],"computer-use":["copilot/*computer-use*","google/*computer-use*","gemini/*computer-use*","openai/*computer-use*"],"copilot":["agent"],"deep-research":["copilot/deep-research*","copilot/o3-deep-research*","copilot/o4-mini-deep-research*","google/deep-research*","gemini/deep-research*","openai/o3-deep-research*","openai/o4-mini-deep-research*"],"gemini":["agent"],"gemini-3-flash":["copilot/gemini-3*flash*","google/gemini-3*flash*","gemini/gemini-3*flash*"],"gemini-3-pro":["copilot/gemini-3*pro*","google/gemini-3*pro*","gemini/gemini-3*pro*"],"gemini-3.1-flash":["copilot/gemini-3.1*flash*","google/gemini-3.1*flash*","gemini/gemini-3.1*flash*"],"gemini-3.1-pro":["copilot/gemini-3.1*pro*","google/gemini-3.1*pro*","gemini/gemini-3.1*pro*"],"gemini-3.5-flash":["copilot/gemini-3.5*flash*","google/gemini-3.5*flash*","gemini/gemini-3.5*flash*"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*","gemini/gemini-*flash*"],"gemini-flash-lite":["copilot/gemini-*flash*lite*","google/gemini-*flash*lite*","gemini/gemini-*flash*lite*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*","gemini/gemini-*pro*"],"gemma":["copilot/gemma*","google/gemma*","gemini/gemma*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"gpt-5.2":["copilot/gpt-5.2*","openai/gpt-5.2*"],"gpt-5.3":["copilot/gpt-5.3*","openai/gpt-5.3*"],"gpt-5.4":["copilot/gpt-5.4*","openai/gpt-5.4*"],"gpt-5.5":["copilot/gpt-5.5*","openai/gpt-5.5*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash-lite"],"opus":["copilot/*opus*","anthropic/*opus*"],"opusplan":["opus?effort=high"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"robotics":["copilot/*robotics*","google/*robotics*","gemini/*robotics*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"],"sonnet-6x":["copilot/*sonnet-4-5-*","anthropic/*sonnet-4-5-*","copilot/*sonnet-4-6*","anthropic/*sonnet-4-6*"],"summarization":["haiku","gpt-5-mini","gemini-flash-lite","mini"],"vision":["copilot/gemini-*image*","gemini/gemini-*image*","copilot/gemini-*flash*","gemini/gemini-*flash*"]}},"container":{"imageTag":"0.25.55,squid=sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca,agent=sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731,api-proxy=sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" + cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="" + if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw" + fi + # shellcheck disable=SC1003 + sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ + -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + env: + AWF_REFLECT_ENABLED: 1 + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.6' }} + GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json + GH_AW_PHASE: agent + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} + GH_AW_VERSION: v0.76.1 + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_AW: true + GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md + GITHUB_WORKSPACE: ${{ github.workspace }} + GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_AUTHOR_NAME: github-actions[bot] + GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_COMMITTER_NAME: github-actions[bot] + XDG_CONFIG_HOME: /home/runner + - name: Detect agent errors + if: always() + id: detect-agent-errors + continue-on-error: true + run: node "${RUNNER_TEMP}/gh-aw/actions/detect_agent_errors.cjs" + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + GITHUB_TOKEN: ${{ github.token }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Copy Copilot session state files to logs + if: always() + continue-on-error: true + run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh" + - name: Stop MCP Gateway + if: always() + continue-on-error: true + env: + MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} + MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} + GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} + run: | + bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID" + - name: Redact secrets in logs + if: always() + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/redact_secrets.cjs'); + await main(); + env: + GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' + SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Append agent step summary + if: always() + run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh" + - name: Copy Safe Outputs + if: always() + env: + GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} + run: | + mkdir -p /tmp/gh-aw + cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true + - name: Ingest agent output + id: collect_output + if: always() + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} + GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_API_URL: ${{ github.api_url }} + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/collect_ndjson_output.cjs'); + await main(); + - name: Parse agent logs for step summary + if: always() + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_copilot_log.cjs'); + await main(); + - name: Parse MCP Gateway logs for step summary + if: always() + id: parse-mcp-gateway + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_mcp_gateway_log.cjs'); + await main(); + - name: Print firewall logs + if: always() + continue-on-error: true + env: + AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs + run: | + # Fix permissions on firewall logs/audit dirs so they can be uploaded as artifacts + # AWF runs with sudo, creating files owned by root + sudo chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true + # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) + if command -v awf &> /dev/null; then + awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" + else + echo 'AWF binary not installed, skipping firewall log summary' + fi + - name: Parse token usage for step summary + if: always() + continue-on-error: true + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_token_usage.cjs'); + await main(); + - name: Print AWF reflect summary + if: always() + continue-on-error: true + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/awf_reflect_summary.cjs'); + await main(); + - name: Write agent output placeholder if missing + if: always() + run: | + if [ ! -f /tmp/gh-aw/agent_output.json ]; then + echo '{"items":[]}' > /tmp/gh-aw/agent_output.json + fi + - name: Upload agent artifacts + if: always() + continue-on-error: true + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: agent + path: | + /tmp/gh-aw/aw-prompts/prompt.txt + /tmp/gh-aw/sandbox/agent/logs/ + /tmp/gh-aw/redacted-urls.log + /tmp/gh-aw/mcp-logs/ + /tmp/gh-aw/proxy-logs/ + !/tmp/gh-aw/proxy-logs/proxy-tls/ + /tmp/gh-aw/agent_usage.json + /tmp/gh-aw/agent-stdio.log + /tmp/gh-aw/pre-agent-audit.txt + /tmp/gh-aw/agent/ + /tmp/gh-aw/github_rate_limits.jsonl + /tmp/gh-aw/safeoutputs.jsonl + /tmp/gh-aw/agent_output.json + /tmp/gh-aw/aw-*.patch + /tmp/gh-aw/aw-*.bundle + /tmp/gh-aw/awf-config.json + /tmp/gh-aw/sandbox/firewall/logs/ + /tmp/gh-aw/sandbox/firewall/audit/ + /tmp/gh-aw/sandbox/firewall/awf-reflect.json + if-no-files-found: ignore + + conclusion: + needs: + - activation + - agent + - detection + - safe_outputs + if: > + always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' || + needs.activation.outputs.stale_lock_file_failed == 'true') + runs-on: ubuntu-slim + permissions: + contents: read + discussions: write + issues: write + pull-requests: write + concurrency: + group: "gh-aw-conclusion-daily-issue-triage" + cancel-in-progress: false + queue: max + outputs: + incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }} + noop_message: ${{ steps.noop.outputs.noop_message }} + tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} + total_count: ${{ steps.missing_tool.outputs.total_count }} + steps: + - name: Setup Scripts + id: setup + uses: github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Daily Issue Triage" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-issue-triage.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.52" + GH_AW_INFO_AWF_VERSION: "v0.25.55" + GH_AW_INFO_BODY_MODIFIED: "false" + GH_AW_INFO_ENGINE_ID: "copilot" + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + id: setup-agent-output-env + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + - name: Process no-op messages + id: noop + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + GH_AW_NOOP_MAX: "1" + GH_AW_WORKFLOW_NAME: "Daily Issue Triage" + GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/daily-issue-triage.md@d7c1dc4b72b00607a67caaffdcc216cb64379cf9" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/d7c1dc4b72b00607a67caaffdcc216cb64379cf9/workflows/daily-issue-triage.md" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} + GH_AW_NOOP_REPORT_AS_ISSUE: "true" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_noop_message.cjs'); + await main(); + - name: Log detection run + id: detection_runs + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Daily Issue Triage" + GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/daily-issue-triage.md@d7c1dc4b72b00607a67caaffdcc216cb64379cf9" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/d7c1dc4b72b00607a67caaffdcc216cb64379cf9/workflows/daily-issue-triage.md" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }} + GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }} + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_detection_runs.cjs'); + await main(); + - name: Record missing tool + id: missing_tool + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + GH_AW_MISSING_TOOL_CREATE_ISSUE: "true" + GH_AW_WORKFLOW_NAME: "Daily Issue Triage" + GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/daily-issue-triage.md@d7c1dc4b72b00607a67caaffdcc216cb64379cf9" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/d7c1dc4b72b00607a67caaffdcc216cb64379cf9/workflows/daily-issue-triage.md" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs'); + await main(); + - name: Record incomplete + id: report_incomplete + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true" + GH_AW_WORKFLOW_NAME: "Daily Issue Triage" + GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/daily-issue-triage.md@d7c1dc4b72b00607a67caaffdcc216cb64379cf9" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/d7c1dc4b72b00607a67caaffdcc216cb64379cf9/workflows/daily-issue-triage.md" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/report_incomplete_handler.cjs'); + await main(); + - name: Handle agent failure + id: handle_agent_failure + if: always() + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Daily Issue Triage" + GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/daily-issue-triage.md@d7c1dc4b72b00607a67caaffdcc216cb64379cf9" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/d7c1dc4b72b00607a67caaffdcc216cb64379cf9/workflows/daily-issue-triage.md" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} + GH_AW_WORKFLOW_ID: "daily-issue-triage" + GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "168" + GH_AW_ENGINE_ID: "copilot" + GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} + GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} + GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }} + GH_AW_EFFECTIVE_TOKENS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.effective_tokens_rate_limit_error || 'false' }} + GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} + GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }} + GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }} + GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }} + GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com" + GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }} + GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }} + GH_AW_GROUP_REPORTS: "false" + GH_AW_FAILURE_REPORT_AS_ISSUE: "true" + GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true" + GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true" + GH_AW_TIMEOUT_MINUTES: "60" + GH_AW_MAX_EFFECTIVE_TOKENS: "25000000" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); + await main(); + + detection: + needs: + - activation + - agent + if: > + always() && needs.agent.result != 'skipped' && (needs.agent.outputs.output_types != '' || needs.agent.outputs.has_patch == 'true') + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }} + detection_reason: ${{ steps.detection_conclusion.outputs.reason }} + detection_success: ${{ steps.detection_conclusion.outputs.success }} + steps: + - name: Setup Scripts + id: setup + uses: github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Daily Issue Triage" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-issue-triage.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.52" + GH_AW_INFO_AWF_VERSION: "v0.25.55" + GH_AW_INFO_BODY_MODIFIED: "false" + GH_AW_INFO_ENGINE_ID: "copilot" + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + id: setup-agent-output-env + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + - name: Checkout repository for patch context + if: needs.agent.outputs.has_patch == 'true' + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + # --- Threat Detection --- + - name: Clean stale firewall files from agent artifact + run: | + rm -rf /tmp/gh-aw/sandbox/firewall/logs + rm -rf /tmp/gh-aw/sandbox/firewall/audit + - name: Download container images + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.55@sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55@sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3 ghcr.io/github/gh-aw-firewall/squid:0.25.55@sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca + - name: Check if detection needed + id: detection_guard + if: always() + env: + OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }} + HAS_PATCH: ${{ needs.agent.outputs.has_patch }} + run: | + if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then + echo "run_detection=true" >> "$GITHUB_OUTPUT" + echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH" + else + echo "run_detection=false" >> "$GITHUB_OUTPUT" + echo "Detection skipped: no agent outputs or patches to analyze" + fi + - name: Clear MCP Config for detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + rm -f "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json" + rm -f /home/runner/.copilot/mcp-config.json + rm -f "$GITHUB_WORKSPACE/.gemini/settings.json" + - name: Prepare threat detection files + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + mkdir -p /tmp/gh-aw/threat-detection/aw-prompts + cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true + cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true + for f in /tmp/gh-aw/aw-*.patch; do + [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true + done + for f in /tmp/gh-aw/aw-*.bundle; do + [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true + done + echo "Prepared threat detection files:" + ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true + - name: Setup threat detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + WORKFLOW_NAME: "Daily Issue Triage" + WORKFLOW_DESCRIPTION: "Scheduled daily triage that processes untriaged CloudStack issues in batches.\nDetects duplicates, filters spam, and assigns CloudStack-specific labels\n(type:*, component:*, Severity:*, status:*), then posts a structured triage report." + HAS_PATCH: ${{ needs.agent.outputs.has_patch }} + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/setup_threat_detection.cjs'); + await main(); + - name: Ensure threat-detection directory and log + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + mkdir -p /tmp/gh-aw/threat-detection + touch /tmp/gh-aw/threat-detection/detection.log + - name: Setup Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: '24' + package-manager-cache: false + - name: Install GitHub Copilot CLI + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.52 + env: + GH_HOST: github.com + - name: Install AWF binary + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.55 + - name: Execute GitHub Copilot CLI + if: always() && steps.detection_guard.outputs.run_detection == 'true' + continue-on-error: true + id: detection_agentic_execution + # Copilot CLI tool arguments (sorted): + timeout-minutes: 20 + run: | + set -o pipefail + printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt + touch /tmp/gh-aw/agent-step-summary.md + GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) + export GH_AW_NODE_BIN + export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" + (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log) + printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.55/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","github.com","host.docker.internal","telemetry.enterprise.githubcopilot.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000},"container":{"imageTag":"0.25.55"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" + cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="" + if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw" + fi + # shellcheck disable=SC1003 + sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ + -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log + env: + AWF_REFLECT_ENABLED: 1 + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || 'claude-sonnet-4.6' }} + GH_AW_PHASE: detection + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_VERSION: v0.76.1 + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_AW: true + GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md + GITHUB_WORKSPACE: ${{ github.workspace }} + GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_AUTHOR_NAME: github-actions[bot] + GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_COMMITTER_NAME: github-actions[bot] + XDG_CONFIG_HOME: /home/runner + - name: Upload threat detection log + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: detection + path: /tmp/gh-aw/threat-detection/detection.log + if-no-files-found: ignore + - name: Parse and conclude threat detection + id: detection_conclusion + if: always() + continue-on-error: true + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} + DETECTION_AGENTIC_EXECUTION_OUTCOME: ${{ steps.detection_agentic_execution.outcome }} + GH_AW_DETECTION_CONTINUE_ON_ERROR: "true" + with: + script: | + try { + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_threat_detection_results.cjs'); + await main(); + } catch (loadErr) { + const continueOnError = process.env.GH_AW_DETECTION_CONTINUE_ON_ERROR !== 'false'; + const detectionExecutionFailed = process.env.DETECTION_AGENTIC_EXECUTION_OUTCOME === 'failure'; + const msg = 'ERR_SYSTEM: \u274C Unexpected error loading threat detection module: ' + (loadErr && loadErr.message ? loadErr.message : String(loadErr)); + core.error(msg); + core.setOutput('reason', 'parse_error'); + if (continueOnError && !detectionExecutionFailed) { + core.warning('\u26A0\uFE0F ' + msg); + core.setOutput('conclusion', 'warning'); + core.setOutput('success', 'false'); + } else { + core.setOutput('conclusion', 'failure'); + core.setOutput('success', 'false'); + core.setFailed(msg); + } + } + + safe_outputs: + needs: + - activation + - agent + - detection + if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' + runs-on: ubuntu-slim + permissions: + contents: read + discussions: write + issues: write + pull-requests: write + timeout-minutes: 15 + env: + GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-issue-triage" + GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }} + GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }} + GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }} + GH_AW_ENGINE_ID: "copilot" + GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }} + GH_AW_ENGINE_VERSION: "1.0.52" + GH_AW_WORKFLOW_ID: "daily-issue-triage" + GH_AW_WORKFLOW_NAME: "Daily Issue Triage" + GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/daily-issue-triage.md@d7c1dc4b72b00607a67caaffdcc216cb64379cf9" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/d7c1dc4b72b00607a67caaffdcc216cb64379cf9/workflows/daily-issue-triage.md" + outputs: + code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} + code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} + comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }} + comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }} + create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} + create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} + steps: + - name: Setup Scripts + id: setup + uses: github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Daily Issue Triage" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-issue-triage.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.52" + GH_AW_INFO_AWF_VERSION: "v0.25.55" + GH_AW_INFO_BODY_MODIFIED: "false" + GH_AW_INFO_ENGINE_ID: "copilot" + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + id: setup-agent-output-env + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + - name: Configure GH_HOST for enterprise compatibility + id: ghes-host-config + shell: bash + run: | + # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct + # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op. + GH_HOST="${GITHUB_SERVER_URL#https://}" + GH_HOST="${GH_HOST#http://}" + echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV" + - name: Process Safe Outputs + id: process_safe_outputs + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }} + GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_API_URL: ${{ github.api_url }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":10,\"target\":\"*\"},\"add_labels\":{\"max\":10,\"target\":\"*\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/safe_output_handler_manager.cjs'); + await main(); + - name: Upload Safe Outputs Items + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: safe-outputs-items + path: | + /tmp/gh-aw/safe-output-items.jsonl + /tmp/gh-aw/temporary-id-map.json + if-no-files-found: ignore diff --git a/.github/workflows/daily-issue-triage.md b/.github/workflows/daily-issue-triage.md new file mode 100644 index 000000000000..719dca1f3c63 --- /dev/null +++ b/.github/workflows/daily-issue-triage.md @@ -0,0 +1,171 @@ +--- +description: | + Scheduled daily triage that processes untriaged CloudStack issues in batches. + Detects duplicates, filters spam, and assigns CloudStack-specific labels + (type:*, component:*, Severity:*, status:*), then posts a structured triage report. + +name: Daily Issue Triage + +on: + schedule: daily around 14:00 on weekdays + workflow_dispatch: + +permissions: read-all + +network: defaults + +safe-outputs: + add-labels: + target: "*" + max: 10 + add-comment: + target: "*" + max: 10 + +tools: + web-fetch: + github: + toolsets: [issues, labels] + min-integrity: none + +source: githubnext/agentics/workflows/daily-issue-triage.md@d7c1dc4b72b00607a67caaffdcc216cb64379cf9 +timeout-minutes: 60 +--- + +# Daily Issue Triage + + + +You are a batch triage assistant for GitHub issues in **${{ github.repository }}** (Apache CloudStack). Your task is to find untriaged issues and triage them one by one. Your triage comments are written for maintainers reviewing the triage, not for the issue author. + +Do not make assumptions beyond what the issue content supports. Do not invent missing context. + +## Step 1: Find untriaged issues + +Use the `search_issues` tool to find open issues that need triage. An issue is considered untriaged if it has **no labels applied**. + +Query: `repo:${{ github.repository }} is:issue is:open no:label` + +Paginate through all results to find untriaged issues. Do not stop at the first page. + +From the results, filter out: +- Issues that already have a triage comment (look for "🎯 Triage report" in comments). **Never retriage an issue that has already been triaged.** +- Issues created by bots (unless they look like real user issues). +- Issues that have any labels already applied (even if they weren't applied by this workflow). + +Process the **oldest untriaged issues first**. Note: this workflow is capped at 10 label-sets and 10 comments per run, so the backlog will drain over several daily runs — that is intentional. + +## Step 2: Fetch labels (once) + +Before triaging any issues, fetch the list of labels available in this repository using the `list_labels` tool. Use this live list for all issues in the batch — only apply labels that actually exist in the repository. + +CloudStack uses a prefixed label taxonomy. Choose from these families: + +- **Type** (pick the single best one): `type:bug`, `type:new-feature`, `type:enhancement`, `type:improvement`, `type:regression`, `type:security`, `type:question`, `type:config`, `type:cleanup` +- **Component** (apply when clearly identifiable; more than one is allowed): e.g. `component:kvm`, `component:vmware`, `component:XenServer`, `component:api`, `component:UI`, `component:networking`, `component:virtual-router`, `component:management-server`, `component:primary-storage`, `component:secondary-storage`, `component:kubernetes`, `component:database`, and others — use the full list returned by `list_labels`. +- **Severity** (bugs only, when assessable): `Severity:BLOCKER`, `Severity:Critical`, `Severity:Major`, `Severity:Minor`, `Severity:Trivial` +- **Duplicate / invalid**: `status:duplicate`, `status:invalid` +- **Help wanted / newcomer-friendly**: `status:Help-wanted` + +## Step 3: Triage each issue + +For each untriaged issue, perform the following steps: + +### 3a: Gather context + +1. Retrieve the full issue content using the `get_issue` tool. +2. Fetch any comments on the issue using the `get_issue_comments` tool. +3. Search for similar issues using the `search_issues` tool. + +### 3b: Spam and quality check + +**Spam and invalid issues:** If the issue is obviously spam, bot-generated, gibberish, or a test issue: +- Apply the `status:invalid` label. +- **Do not close the issue** — closing is a human decision. Note in the report that it looks like spam/invalid so a maintainer can act. +- Move to the next issue. + +**Incomplete issues:** If the issue lacks enough detail for meaningful triage, add a comment that politely asks the author to provide the missing information: +- For bugs: steps to reproduce, expected vs actual behavior, logs/errors, environment details (CloudStack version, hypervisor, etc.). +- For other issue types: equivalent details that would make the report actionable. +- Apply a `type:question` label if appropriate. +- Be specific about what is missing and why it is needed. +- Move to the next issue. + +### 3c: Select labels + +- Be cautious with labels; they can trigger automation. +- Choose a single `type:*` label that best reflects the issue's nature. +- Add `component:*` label(s) when the affected area is clear from the content. +- Add a `Severity:*` label for bugs when severity can be reasonably assessed. +- Do not apply labels that do not exist in the repository. +- It is better to under-label than to speculatively add labels. + +### 3d: Detect duplicates and related issues + +- Review the similar issues found in Step 3a. +- Classify matches as: + - **Duplicate** (high confidence): the issue describes the same problem as an existing open issue. Include up to 3. + - **Related**: similar domain or adjacent problem, but not a duplicate. Include up to 3. +- If a high-confidence duplicate is found, apply the `status:duplicate` label. +- If no similar issues are found, state that explicitly in your report. + +### 3e: Assess coding agent suitability + +Assess whether the issue is suitable for automated coding agent assignment: +- **Suitable**: clear requirements, sufficient context, well-defined success criteria, self-contained scope. +- **Needs more info**: potentially suitable but missing details needed to start. +- **Not suitable**: requires investigation, design decisions, extensive coordination, or policy/architectural choices. + +### 3f: Additional analysis + +- Search the web for relevant documentation, error messages, or known solutions if applicable. +- Write notes, debugging strategies, and/or reproduction steps relevant to the issue. +- Suggest resources or links that might help resolve the issue. + +### 3g: Apply results and post comment + +Apply all triage results for this issue: +- Use `update_issue` to apply the chosen labels. +- Add an issue comment with the triage report using the format below. + +Then move to the next issue. + +## Processing order + +1. Fetch available labels (Step 2, once at the start). +2. Find untriaged issues (Step 1). +3. For each issue (oldest first), run Step 3 (gather, check, label, detect duplicates, comment). + +## Comment format + +Use this structure for each triage comment. Use collapsed sections to keep it tidy. + +```markdown +## 🎯 Triage report + +{2-3 sentence summary to help a maintainer quickly grasp the issue.} + +### 📊 Assessment + +| Dimension | Value | Reasoning | +|---|---|---| +| **Type** | [type:* label or "none"] | [brief] | +| **Component** | [component:* label(s) or "none"] | [brief] | +| **Severity** | [Severity:* label or "n/a"] | [brief] | +| **Labels** | [all labels applied or "none"] | [brief] | +| **Coding agent** | [Suitable / Needs more info / Not suitable] | [brief] | + +### 🔗 Similar issues + +- issue-url (duplicate/related) — [brief explanation] + +
💡 Notes and suggestions + +{Debugging strategies, reproduction steps, resource links, sub-task checklists, nudges for the team.} + +
+``` + +If no similar issues were found, omit the "Similar issues" section. If there are no notes to add, omit the collapsed section. + +**Important**: Never close issues. Only apply labels and post comments. diff --git a/.github/workflows/daily-repo-status.lock.yml b/.github/workflows/daily-repo-status.lock.yml new file mode 100644 index 000000000000..0992d3b67de0 --- /dev/null +++ b/.github/workflows/daily-repo-status.lock.yml @@ -0,0 +1,1394 @@ +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"bcecce6f1d9f8df2b3eca9eb7bb1fdbac13c396c240a2dc802a96546f435b969","compiler_version":"v0.76.1","strict":true,"agent_id":"copilot","agent_model":"claude-haiku-4.5"} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"46d564922b082d0db93244972e8005ea6904ee5f","version":"v0.76.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.55","digest":"sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.25.55@sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55","digest":"sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55@sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.55","digest":"sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.25.55@sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.19","digest":"sha256:a6c890d7c24d7190c9ef97b9c954cc4cffaae6b01c371ced1f959f1370b1f68f","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.19@sha256:a6c890d7c24d7190c9ef97b9c954cc4cffaae6b01c371ced1f959f1370b1f68f"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4","digest":"sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4"},{"image":"node:lts-alpine","digest":"sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14","pinned_image":"node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14"}]} +# ___ _ _ +# / _ \ | | (_) +# | |_| | __ _ ___ _ __ | |_ _ ___ +# | _ |/ _` |/ _ \ '_ \| __| |/ __| +# | | | | (_| | __/ | | | |_| | (__ +# \_| |_/\__, |\___|_| |_|\__|_|\___| +# __/ | +# _ _ |___/ +# | | | | / _| | +# | | | | ___ _ __ _ __| |_| | _____ ____ +# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| +# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ +# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ +# +# This file was automatically generated by gh-aw (v0.76.1). DO NOT EDIT. +# +# To update this file, edit githubnext/agentics/workflows/repo-status.md@main and run: +# gh aw compile +# Not all edits will cause changes to this file. +# +# For more information: https://github.github.com/gh-aw/introduction/overview/ +# +# This workflow creates daily repo status reports. It gathers recent repository +# activity (issues, PRs, discussions, releases, code changes) and generates +# engaging GitHub issues with productivity insights, community highlights, +# and project recommendations. +# +# Source: githubnext/agentics/workflows/repo-status.md@main +# +# Secrets used: +# - COPILOT_GITHUB_TOKEN +# - GH_AW_GITHUB_MCP_SERVER_TOKEN +# - GH_AW_GITHUB_TOKEN +# - GITHUB_TOKEN +# +# Custom actions used: +# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 +# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 +# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 +# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 +# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 +# - github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8 +# +# Container images used: +# - ghcr.io/github/gh-aw-firewall/agent:0.25.55@sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731 +# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55@sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3 +# - ghcr.io/github/gh-aw-firewall/squid:0.25.55@sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca +# - ghcr.io/github/gh-aw-mcpg:v0.3.19@sha256:a6c890d7c24d7190c9ef97b9c954cc4cffaae6b01c371ced1f959f1370b1f68f +# - ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4 +# - node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14 + +name: "Repo Status" +on: + schedule: + - cron: "11 19 * * *" + # Friendly format: daily (scattered) + workflow_dispatch: + inputs: + aw_context: + default: "" + description: "Agent caller context (used internally by Agentic Workflows)." + required: false + type: string + +permissions: {} + +concurrency: + group: "gh-aw-${{ github.workflow }}" + +run-name: "Repo Status" + +jobs: + activation: + runs-on: ubuntu-slim + permissions: + actions: read + contents: read + outputs: + comment_id: "" + comment_repo: "" + engine_id: ${{ steps.generate_aw_info.outputs.engine_id }} + lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }} + model: ${{ steps.generate_aw_info.outputs.model }} + secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} + setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} + setup-span-id: ${{ steps.setup.outputs.span-id }} + setup-trace-id: ${{ steps.setup.outputs.trace-id }} + stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }} + steps: + - name: Setup Scripts + id: setup + uses: github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Repo Status" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-repo-status.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.52" + GH_AW_INFO_AWF_VERSION: "v0.25.55" + GH_AW_INFO_BODY_MODIFIED: "false" + GH_AW_INFO_ENGINE_ID: "copilot" + - name: Generate agentic run info + id: generate_aw_info + env: + GH_AW_INFO_ENGINE_ID: "copilot" + GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" + GH_AW_INFO_MODEL: "claude-haiku-4.5" + GH_AW_INFO_VERSION: "1.0.52" + GH_AW_INFO_AGENT_VERSION: "1.0.52" + GH_AW_INFO_CLI_VERSION: "v0.76.1" + GH_AW_INFO_WORKFLOW_NAME: "Repo Status" + GH_AW_INFO_EXPERIMENTAL: "false" + GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" + GH_AW_INFO_STAGED: "false" + GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]' + GH_AW_INFO_FIREWALL_ENABLED: "true" + GH_AW_INFO_AWF_VERSION: "v0.25.55" + GH_AW_INFO_AWMG_VERSION: "" + GH_AW_INFO_FIREWALL_TYPE: "squid" + GH_AW_INFO_FRONTMATTER_SOURCE: "githubnext/agentics/workflows/repo-status.md@main" + GH_AW_INFO_BODY_MODIFIED: "false" + GH_AW_COMPILED_STRICT: "true" + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs'); + await main(core, context); + - name: Validate COPILOT_GITHUB_TOKEN secret + id: validate-secret + run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_multi_secret.sh" COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default + env: + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + - name: Checkout .github and .agents folders + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + sparse-checkout: | + .github + .agents + .antigravity + .claude + .codex + .crush + .gemini + .opencode + .pi + sparse-checkout-cone-mode: true + fetch-depth: 1 + - name: Save agent config folders for base branch restoration + env: + GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi" + GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc" + # poutine:ignore untrusted_checkout_exec + run: bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh" + - name: Check workflow lock file + id: check-lock-file + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_WORKFLOW_FILE: "daily-repo-status.lock.yml" + GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}" + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/check_workflow_timestamp_api.cjs'); + await main(); + - name: Check compile-agentic version + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_COMPILED_VERSION: "v0.76.1" + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/check_version_updates.cjs'); + await main(); + - name: Create prompt with built-in context + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl + GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + # poutine:ignore untrusted_checkout_exec + run: | + bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" + { + cat << 'GH_AW_PROMPT_eeb322738661ed58_EOF' + + GH_AW_PROMPT_eeb322738661ed58_EOF + cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" + cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" + cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" + cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" + cat << 'GH_AW_PROMPT_eeb322738661ed58_EOF' + + Tools: create_issue, missing_tool, missing_data, noop + + GH_AW_PROMPT_eeb322738661ed58_EOF + cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" + cat << 'GH_AW_PROMPT_eeb322738661ed58_EOF' + + The following GitHub context information is available for this workflow: + {{#if github.actor}} + - **actor**: __GH_AW_GITHUB_ACTOR__ + {{/if}} + {{#if github.repository}} + - **repository**: __GH_AW_GITHUB_REPOSITORY__ + {{/if}} + {{#if github.workspace}} + - **workspace**: __GH_AW_GITHUB_WORKSPACE__ + {{/if}} + {{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}} + - **issue-number**: #__GH_AW_EXPR_802A9F6A__ + {{/if}} + {{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}} + - **discussion-number**: #__GH_AW_EXPR_1A3A194A__ + {{/if}} + {{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}} + - **pull-request-number**: #__GH_AW_EXPR_463A214A__ + {{/if}} + {{#if github.event.comment.id || github.aw.context.comment_id}} + - **comment-id**: __GH_AW_EXPR_FF1D34CE__ + {{/if}} + {{#if github.run_id}} + - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ + {{/if}} + + + GH_AW_PROMPT_eeb322738661ed58_EOF + cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" + cat << 'GH_AW_PROMPT_eeb322738661ed58_EOF' + + {{#runtime-import .github/workflows/daily-repo-status.md}} + GH_AW_PROMPT_eeb322738661ed58_EOF + } > "$GH_AW_PROMPT" + - name: Interpolate variables and render templates + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_ENGINE_ID: "copilot" + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/interpolate_prompt.cjs'); + await main(); + - name: Substitute placeholders + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools' + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + + const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs'); + + // Call the substitution function + return await substitutePlaceholders({ + file: process.env.GH_AW_PROMPT, + substitutions: { + GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A, + GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A, + GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A, + GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE, + GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, + GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, + GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, + GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, + GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST + } + }); + - name: Validate prompt placeholders + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + # poutine:ignore untrusted_checkout_exec + run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh" + - name: Print prompt + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + # poutine:ignore untrusted_checkout_exec + run: bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh" + - name: Upload activation artifact + if: success() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: activation + include-hidden-files: true + path: | + /tmp/gh-aw/aw_info.json + /tmp/gh-aw/aw-prompts/prompt.txt + /tmp/gh-aw/aw-prompts/prompt-template.txt + /tmp/gh-aw/aw-prompts/prompt-import-tree.json + /tmp/gh-aw/github_rate_limits.jsonl + /tmp/gh-aw/base + /tmp/gh-aw/.github/agents + /tmp/gh-aw/.github/skills + if-no-files-found: ignore + retention-days: 1 + + agent: + needs: activation + runs-on: ubuntu-latest + permissions: + contents: read + issues: read + pull-requests: read + concurrency: + group: "gh-aw-copilot-${{ github.workflow }}" + env: + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + GH_AW_ASSETS_ALLOWED_EXTS: "" + GH_AW_ASSETS_BRANCH: "" + GH_AW_ASSETS_MAX_SIZE_KB: 0 + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + GH_AW_WORKFLOW_ID_SANITIZED: dailyrepostatus + outputs: + agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} + checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} + effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }} + effective_tokens_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.effective_tokens_rate_limit_error || 'false' }} + has_patch: ${{ steps.collect_output.outputs.has_patch }} + inference_access_error: ${{ steps.detect-agent-errors.outputs.inference_access_error || 'false' }} + mcp_policy_error: ${{ steps.detect-agent-errors.outputs.mcp_policy_error || 'false' }} + model: ${{ needs.activation.outputs.model }} + model_not_supported_error: ${{ steps.detect-agent-errors.outputs.model_not_supported_error || 'false' }} + output: ${{ steps.collect_output.outputs.output }} + output_types: ${{ steps.collect_output.outputs.output_types }} + setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} + setup-span-id: ${{ steps.setup.outputs.span-id }} + setup-trace-id: ${{ steps.setup.outputs.trace-id }} + steps: + - name: Setup Scripts + id: setup + uses: github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Repo Status" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-repo-status.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.52" + GH_AW_INFO_AWF_VERSION: "v0.25.55" + GH_AW_INFO_BODY_MODIFIED: "false" + GH_AW_INFO_ENGINE_ID: "copilot" + - name: Set runtime paths + id: set-runtime-paths + run: | + { + echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl" + echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" + echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json" + } >> "$GITHUB_OUTPUT" + - name: Checkout repository + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + - name: Create gh-aw temp directory + run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh" + - name: Configure gh CLI for GitHub Enterprise + run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh" + env: + GH_TOKEN: ${{ github.token }} + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + GITHUB_TOKEN: ${{ github.token }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Checkout PR branch + id: checkout-pr + if: | + github.event.pull_request || github.event.issue.pull_request + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + with: + github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs'); + await main(); + - name: Install GitHub Copilot CLI + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.52 + env: + GH_HOST: github.com + - name: Install AWF binary + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.55 + - name: Parse integrity filter lists + id: parse-guard-vars + env: + GH_AW_BLOCKED_USERS_VAR: ${{ vars.GH_AW_GITHUB_BLOCKED_USERS || '' }} + GH_AW_TRUSTED_USERS_VAR: ${{ vars.GH_AW_GITHUB_TRUSTED_USERS || '' }} + GH_AW_APPROVAL_LABELS_VAR: ${{ vars.GH_AW_GITHUB_APPROVAL_LABELS || '' }} + run: bash "${RUNNER_TEMP}/gh-aw/actions/parse_guard_list.sh" + - name: Download activation artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: activation + path: /tmp/gh-aw + - name: Restore agent config folders from base branch + if: steps.checkout-pr.outcome == 'success' + env: + GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi" + GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc" + run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh" + - name: Restore inline sub-agents from activation artifact + env: + GH_AW_SUB_AGENT_DIR: ".github/agents" + GH_AW_SUB_AGENT_EXT: ".agent.md" + run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh" + - name: Restore inline skills from activation artifact + env: + GH_AW_SKILL_DIR: ".github/skills" + run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh" + - name: Download container images + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.55@sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55@sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3 ghcr.io/github/gh-aw-firewall/squid:0.25.55@sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca ghcr.io/github/gh-aw-mcpg:v0.3.19@sha256:a6c890d7c24d7190c9ef97b9c954cc4cffaae6b01c371ced1f959f1370b1f68f ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4 node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14 + - name: Generate Safe Outputs Config + run: | + mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" + mkdir -p /tmp/gh-aw/safeoutputs + mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_51571b44da85874d_EOF' + {"create_issue":{"close_older_issues":true,"labels":["report","daily-status"],"max":1,"title_prefix":"[repo-status] "},"create_report_incomplete_issue":{},"mentions":{"enabled":false},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}} + GH_AW_SAFE_OUTPUTS_CONFIG_51571b44da85874d_EOF + - name: Generate Safe Outputs Tools + env: + GH_AW_TOOLS_META_JSON: | + { + "description_suffixes": { + "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[repo-status] \". Labels [\"report\" \"daily-status\"] will be automatically added." + }, + "repo_params": {}, + "dynamic_tools": [] + } + GH_AW_VALIDATION_JSON: | + { + "create_issue": { + "defaultMax": 1, + "fields": { + "body": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "fields": { + "type": "array" + }, + "labels": { + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 + }, + "parent": { + "issueOrPRNumber": true + }, + "repo": { + "type": "string", + "maxLength": 256 + }, + "temporary_id": { + "type": "string" + }, + "title": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 128 + } + } + }, + "missing_data": { + "defaultMax": 20, + "fields": { + "alternatives": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "context": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "data_type": { + "type": "string", + "sanitize": true, + "maxLength": 128 + }, + "reason": { + "type": "string", + "sanitize": true, + "maxLength": 256 + } + } + }, + "missing_tool": { + "defaultMax": 20, + "fields": { + "alternatives": { + "type": "string", + "sanitize": true, + "maxLength": 512 + }, + "reason": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "tool": { + "type": "string", + "sanitize": true, + "maxLength": 128 + } + } + }, + "noop": { + "defaultMax": 1, + "fields": { + "message": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + } + } + }, + "report_incomplete": { + "defaultMax": 5, + "fields": { + "details": { + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "reason": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 1024 + } + } + } + } + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_safe_outputs_tools.cjs'); + await main(); + - name: Generate Safe Outputs MCP Server Config + id: safe-outputs-config + run: | + # Generate a secure random API key (360 bits of entropy, 40+ chars) + # Mask immediately to prevent timing vulnerabilities + API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${API_KEY}" + + PORT=3001 + + # Set outputs for next steps + { + echo "safe_outputs_api_key=${API_KEY}" + echo "safe_outputs_port=${PORT}" + } >> "$GITHUB_OUTPUT" + + echo "Safe Outputs MCP server will run on port ${PORT}" + + - name: Start Safe Outputs MCP HTTP Server + id: safe-outputs-start + env: + DEBUG: '*' + GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }} + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/tools.json + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/config.json + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + run: | + # Environment variables are set above to prevent template injection + export DEBUG + export GH_AW_SAFE_OUTPUTS + export GH_AW_SAFE_OUTPUTS_PORT + export GH_AW_SAFE_OUTPUTS_API_KEY + export GH_AW_SAFE_OUTPUTS_TOOLS_PATH + export GH_AW_SAFE_OUTPUTS_CONFIG_PATH + export GH_AW_MCP_LOG_DIR + + bash "${RUNNER_TEMP}/gh-aw/actions/start_safe_outputs_server.sh" + + - name: Start MCP Gateway + id: start-mcp-gateway + env: + GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + run: | + set -eo pipefail + mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config" + + # Export gateway environment variables for MCP config and gateway script + export MCP_GATEWAY_PORT="8080" + export MCP_GATEWAY_DOMAIN="host.docker.internal" + export MCP_GATEWAY_HOST_DOMAIN="localhost" + MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${MCP_GATEWAY_API_KEY}" + export MCP_GATEWAY_API_KEY + export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" + mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" + export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" + export DEBUG="*" + + export GH_AW_ENGINE="copilot" + MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0') + MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0') + case "${DOCKER_HOST:-}" in + unix://* ) DOCKER_SOCK_PATH="${DOCKER_HOST#unix://}" ;; + /* ) DOCKER_SOCK_PATH="$DOCKER_HOST" ;; + * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;; + esac + DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0') + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.3.19' + + mkdir -p /home/runner/.copilot + GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) + cat << GH_AW_MCP_CONFIG_4b3a7789a6eea081_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + { + "mcpServers": { + "github": { + "type": "stdio", + "container": "ghcr.io/github/github-mcp-server:v1.0.4", + "env": { + "GITHUB_HOST": "\${GITHUB_SERVER_URL}", + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", + "GITHUB_READ_ONLY": "1", + "GITHUB_TOOLSETS": "context,repos,issues,pull_requests" + }, + "guard-policies": { + "allow-only": { + "approval-labels": ${{ steps.parse-guard-vars.outputs.approval_labels }}, + "blocked-users": ${{ steps.parse-guard-vars.outputs.blocked_users }}, + "min-integrity": "none", + "repos": "all", + "trusted-users": ${{ steps.parse-guard-vars.outputs.trusted_users }} + } + } + }, + "safeoutputs": { + "type": "http", + "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", + "headers": { + "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" + }, + "guard-policies": { + "write-sink": { + "accept": [ + "*" + ] + } + } + } + }, + "gateway": { + "port": $MCP_GATEWAY_PORT, + "domain": "${MCP_GATEWAY_DOMAIN}", + "apiKey": "${MCP_GATEWAY_API_KEY}", + "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" + } + } + GH_AW_MCP_CONFIG_4b3a7789a6eea081_EOF + - name: Mount MCP servers as CLIs + id: mount-mcp-clis + continue-on-error: true + env: + MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} + MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }} + MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs'); + await main(); + - name: Clean credentials + continue-on-error: true + run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh" + - name: Audit pre-agent workspace + id: pre_agent_audit + continue-on-error: true + run: bash "${RUNNER_TEMP}/gh-aw/actions/audit_pre_agent_workspace.sh" + - name: Execute GitHub Copilot CLI + id: agentic_execution + # Copilot CLI tool arguments (sorted): + timeout-minutes: 20 + run: | + set -o pipefail + printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt + touch /tmp/gh-aw/agent-step-summary.md + GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) + export GH_AW_NODE_BIN + export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" + (umask 177 && touch /tmp/gh-aw/agent-stdio.log) + printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.55/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.com","crl.geotrust.com","crl.globalsign.com","crl.identrust.com","crl.sectigo.com","crl.thawte.com","crl.usertrust.com","crl.verisign.com","crl3.digicert.com","crl4.digicert.com","crls.ssl.com","github.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.com","ocsp.digicert.com","ocsp.geotrust.com","ocsp.globalsign.com","ocsp.identrust.com","ocsp.sectigo.com","ocsp.ssl.com","ocsp.thawte.com","ocsp.usertrust.com","ocsp.verisign.com","packagecloud.io","packages.cloud.google.com","packages.microsoft.com","ppa.launchpad.net","raw.githubusercontent.com","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","telemetry.enterprise.githubcopilot.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000,"models":{"agent":["sonnet-6x","gpt-5.4","gpt-5.3","gemini-pro","any"],"antigravity":["copilot/antigravity*","google/antigravity*","gemini/antigravity*"],"any":["copilot/*","anthropic/*","openai/*","google/*","gemini/*"],"claude":["agent"],"codex":["agent"],"coding":["copilot/gpt-5*codex*","openai/gpt-5*codex*","gpt-5-codex"],"computer-use":["copilot/*computer-use*","google/*computer-use*","gemini/*computer-use*","openai/*computer-use*"],"copilot":["agent"],"deep-research":["copilot/deep-research*","copilot/o3-deep-research*","copilot/o4-mini-deep-research*","google/deep-research*","gemini/deep-research*","openai/o3-deep-research*","openai/o4-mini-deep-research*"],"gemini":["agent"],"gemini-3-flash":["copilot/gemini-3*flash*","google/gemini-3*flash*","gemini/gemini-3*flash*"],"gemini-3-pro":["copilot/gemini-3*pro*","google/gemini-3*pro*","gemini/gemini-3*pro*"],"gemini-3.1-flash":["copilot/gemini-3.1*flash*","google/gemini-3.1*flash*","gemini/gemini-3.1*flash*"],"gemini-3.1-pro":["copilot/gemini-3.1*pro*","google/gemini-3.1*pro*","gemini/gemini-3.1*pro*"],"gemini-3.5-flash":["copilot/gemini-3.5*flash*","google/gemini-3.5*flash*","gemini/gemini-3.5*flash*"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*","gemini/gemini-*flash*"],"gemini-flash-lite":["copilot/gemini-*flash*lite*","google/gemini-*flash*lite*","gemini/gemini-*flash*lite*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*","gemini/gemini-*pro*"],"gemma":["copilot/gemma*","google/gemma*","gemini/gemma*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"gpt-5.2":["copilot/gpt-5.2*","openai/gpt-5.2*"],"gpt-5.3":["copilot/gpt-5.3*","openai/gpt-5.3*"],"gpt-5.4":["copilot/gpt-5.4*","openai/gpt-5.4*"],"gpt-5.5":["copilot/gpt-5.5*","openai/gpt-5.5*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash-lite"],"opus":["copilot/*opus*","anthropic/*opus*"],"opusplan":["opus?effort=high"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"robotics":["copilot/*robotics*","google/*robotics*","gemini/*robotics*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"],"sonnet-6x":["copilot/*sonnet-4-5-*","anthropic/*sonnet-4-5-*","copilot/*sonnet-4-6*","anthropic/*sonnet-4-6*"],"summarization":["haiku","gpt-5-mini","gemini-flash-lite","mini"],"vision":["copilot/gemini-*image*","gemini/gemini-*image*","copilot/gemini-*flash*","gemini/gemini-*flash*"]}},"container":{"imageTag":"0.25.55,squid=sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca,agent=sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731,api-proxy=sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" + cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="" + if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw" + fi + # shellcheck disable=SC1003 + sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ + -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + env: + AWF_REFLECT_ENABLED: 1 + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: claude-haiku-4.5 + GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json + GH_AW_PHASE: agent + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} + GH_AW_VERSION: v0.76.1 + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_AW: true + GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md + GITHUB_WORKSPACE: ${{ github.workspace }} + GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_AUTHOR_NAME: github-actions[bot] + GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_COMMITTER_NAME: github-actions[bot] + XDG_CONFIG_HOME: /home/runner + - name: Detect agent errors + if: always() + id: detect-agent-errors + continue-on-error: true + run: node "${RUNNER_TEMP}/gh-aw/actions/detect_agent_errors.cjs" + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + GITHUB_TOKEN: ${{ github.token }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Copy Copilot session state files to logs + if: always() + continue-on-error: true + run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh" + - name: Stop MCP Gateway + if: always() + continue-on-error: true + env: + MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} + MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} + GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} + run: | + bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID" + - name: Redact secrets in logs + if: always() + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/redact_secrets.cjs'); + await main(); + env: + GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' + SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Append agent step summary + if: always() + run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh" + - name: Copy Safe Outputs + if: always() + env: + GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} + run: | + mkdir -p /tmp/gh-aw + cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true + - name: Ingest agent output + id: collect_output + if: always() + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} + GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" + GH_AW_ALLOWED_GITHUB_REFS: "" + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_API_URL: ${{ github.api_url }} + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/collect_ndjson_output.cjs'); + await main(); + - name: Parse agent logs for step summary + if: always() + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_copilot_log.cjs'); + await main(); + - name: Parse MCP Gateway logs for step summary + if: always() + id: parse-mcp-gateway + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_mcp_gateway_log.cjs'); + await main(); + - name: Print firewall logs + if: always() + continue-on-error: true + env: + AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs + run: | + # Fix permissions on firewall logs/audit dirs so they can be uploaded as artifacts + # AWF runs with sudo, creating files owned by root + sudo chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true + # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) + if command -v awf &> /dev/null; then + awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" + else + echo 'AWF binary not installed, skipping firewall log summary' + fi + - name: Parse token usage for step summary + if: always() + continue-on-error: true + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_token_usage.cjs'); + await main(); + - name: Print AWF reflect summary + if: always() + continue-on-error: true + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/awf_reflect_summary.cjs'); + await main(); + - name: Write agent output placeholder if missing + if: always() + run: | + if [ ! -f /tmp/gh-aw/agent_output.json ]; then + echo '{"items":[]}' > /tmp/gh-aw/agent_output.json + fi + - name: Upload agent artifacts + if: always() + continue-on-error: true + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: agent + path: | + /tmp/gh-aw/aw-prompts/prompt.txt + /tmp/gh-aw/sandbox/agent/logs/ + /tmp/gh-aw/redacted-urls.log + /tmp/gh-aw/mcp-logs/ + /tmp/gh-aw/proxy-logs/ + !/tmp/gh-aw/proxy-logs/proxy-tls/ + /tmp/gh-aw/agent_usage.json + /tmp/gh-aw/agent-stdio.log + /tmp/gh-aw/pre-agent-audit.txt + /tmp/gh-aw/agent/ + /tmp/gh-aw/github_rate_limits.jsonl + /tmp/gh-aw/safeoutputs.jsonl + /tmp/gh-aw/agent_output.json + /tmp/gh-aw/aw-*.patch + /tmp/gh-aw/aw-*.bundle + /tmp/gh-aw/awf-config.json + /tmp/gh-aw/sandbox/firewall/logs/ + /tmp/gh-aw/sandbox/firewall/audit/ + /tmp/gh-aw/sandbox/firewall/awf-reflect.json + if-no-files-found: ignore + + conclusion: + needs: + - activation + - agent + - detection + - safe_outputs + if: > + always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' || + needs.activation.outputs.stale_lock_file_failed == 'true') + runs-on: ubuntu-slim + permissions: + contents: read + issues: write + concurrency: + group: "gh-aw-conclusion-daily-repo-status" + cancel-in-progress: false + queue: max + outputs: + incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }} + noop_message: ${{ steps.noop.outputs.noop_message }} + tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} + total_count: ${{ steps.missing_tool.outputs.total_count }} + steps: + - name: Setup Scripts + id: setup + uses: github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Repo Status" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-repo-status.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.52" + GH_AW_INFO_AWF_VERSION: "v0.25.55" + GH_AW_INFO_BODY_MODIFIED: "false" + GH_AW_INFO_ENGINE_ID: "copilot" + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + id: setup-agent-output-env + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + - name: Process no-op messages + id: noop + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + GH_AW_NOOP_MAX: "1" + GH_AW_WORKFLOW_NAME: "Repo Status" + GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/repo-status.md@main" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/main/workflows/repo-status.md" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} + GH_AW_NOOP_REPORT_AS_ISSUE: "true" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_noop_message.cjs'); + await main(); + - name: Log detection run + id: detection_runs + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Repo Status" + GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/repo-status.md@main" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/main/workflows/repo-status.md" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }} + GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }} + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_detection_runs.cjs'); + await main(); + - name: Record missing tool + id: missing_tool + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + GH_AW_MISSING_TOOL_CREATE_ISSUE: "true" + GH_AW_WORKFLOW_NAME: "Repo Status" + GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/repo-status.md@main" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/main/workflows/repo-status.md" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs'); + await main(); + - name: Record incomplete + id: report_incomplete + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true" + GH_AW_WORKFLOW_NAME: "Repo Status" + GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/repo-status.md@main" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/main/workflows/repo-status.md" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/report_incomplete_handler.cjs'); + await main(); + - name: Handle agent failure + id: handle_agent_failure + if: always() + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Repo Status" + GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/repo-status.md@main" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/main/workflows/repo-status.md" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} + GH_AW_WORKFLOW_ID: "daily-repo-status" + GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "168" + GH_AW_ENGINE_ID: "copilot" + GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} + GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} + GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }} + GH_AW_EFFECTIVE_TOKENS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.effective_tokens_rate_limit_error || 'false' }} + GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} + GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }} + GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }} + GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }} + GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com" + GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }} + GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }} + GH_AW_GROUP_REPORTS: "false" + GH_AW_FAILURE_REPORT_AS_ISSUE: "true" + GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true" + GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true" + GH_AW_TIMEOUT_MINUTES: "20" + GH_AW_MAX_EFFECTIVE_TOKENS: "25000000" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); + await main(); + + detection: + needs: + - activation + - agent + if: > + always() && needs.agent.result != 'skipped' && (needs.agent.outputs.output_types != '' || needs.agent.outputs.has_patch == 'true') + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }} + detection_reason: ${{ steps.detection_conclusion.outputs.reason }} + detection_success: ${{ steps.detection_conclusion.outputs.success }} + steps: + - name: Setup Scripts + id: setup + uses: github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Repo Status" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-repo-status.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.52" + GH_AW_INFO_AWF_VERSION: "v0.25.55" + GH_AW_INFO_BODY_MODIFIED: "false" + GH_AW_INFO_ENGINE_ID: "copilot" + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + id: setup-agent-output-env + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + - name: Checkout repository for patch context + if: needs.agent.outputs.has_patch == 'true' + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + # --- Threat Detection --- + - name: Clean stale firewall files from agent artifact + run: | + rm -rf /tmp/gh-aw/sandbox/firewall/logs + rm -rf /tmp/gh-aw/sandbox/firewall/audit + - name: Download container images + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.55@sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55@sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3 ghcr.io/github/gh-aw-firewall/squid:0.25.55@sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca + - name: Check if detection needed + id: detection_guard + if: always() + env: + OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }} + HAS_PATCH: ${{ needs.agent.outputs.has_patch }} + run: | + if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then + echo "run_detection=true" >> "$GITHUB_OUTPUT" + echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH" + else + echo "run_detection=false" >> "$GITHUB_OUTPUT" + echo "Detection skipped: no agent outputs or patches to analyze" + fi + - name: Clear MCP Config for detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + rm -f "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json" + rm -f /home/runner/.copilot/mcp-config.json + rm -f "$GITHUB_WORKSPACE/.gemini/settings.json" + - name: Prepare threat detection files + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + mkdir -p /tmp/gh-aw/threat-detection/aw-prompts + cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true + cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true + for f in /tmp/gh-aw/aw-*.patch; do + [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true + done + for f in /tmp/gh-aw/aw-*.bundle; do + [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true + done + echo "Prepared threat detection files:" + ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true + - name: Setup threat detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + WORKFLOW_NAME: "Repo Status" + WORKFLOW_DESCRIPTION: "This workflow creates daily repo status reports. It gathers recent repository\nactivity (issues, PRs, discussions, releases, code changes) and generates\nengaging GitHub issues with productivity insights, community highlights,\nand project recommendations." + HAS_PATCH: ${{ needs.agent.outputs.has_patch }} + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/setup_threat_detection.cjs'); + await main(); + - name: Ensure threat-detection directory and log + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + mkdir -p /tmp/gh-aw/threat-detection + touch /tmp/gh-aw/threat-detection/detection.log + - name: Setup Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: '24' + package-manager-cache: false + - name: Install GitHub Copilot CLI + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.52 + env: + GH_HOST: github.com + - name: Install AWF binary + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.55 + - name: Execute GitHub Copilot CLI + if: always() && steps.detection_guard.outputs.run_detection == 'true' + continue-on-error: true + id: detection_agentic_execution + # Copilot CLI tool arguments (sorted): + timeout-minutes: 20 + run: | + set -o pipefail + printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt + touch /tmp/gh-aw/agent-step-summary.md + GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) + export GH_AW_NODE_BIN + export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" + (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log) + printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.55/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","github.com","host.docker.internal","telemetry.enterprise.githubcopilot.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000},"container":{"imageTag":"0.25.55"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" + cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="" + if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw" + fi + # shellcheck disable=SC1003 + sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ + -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log + env: + AWF_REFLECT_ENABLED: 1 + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: claude-haiku-4.5 + GH_AW_PHASE: detection + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_VERSION: v0.76.1 + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_AW: true + GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md + GITHUB_WORKSPACE: ${{ github.workspace }} + GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_AUTHOR_NAME: github-actions[bot] + GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_COMMITTER_NAME: github-actions[bot] + XDG_CONFIG_HOME: /home/runner + - name: Upload threat detection log + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: detection + path: /tmp/gh-aw/threat-detection/detection.log + if-no-files-found: ignore + - name: Parse and conclude threat detection + id: detection_conclusion + if: always() + continue-on-error: true + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} + DETECTION_AGENTIC_EXECUTION_OUTCOME: ${{ steps.detection_agentic_execution.outcome }} + GH_AW_DETECTION_CONTINUE_ON_ERROR: "true" + with: + script: | + try { + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_threat_detection_results.cjs'); + await main(); + } catch (loadErr) { + const continueOnError = process.env.GH_AW_DETECTION_CONTINUE_ON_ERROR !== 'false'; + const detectionExecutionFailed = process.env.DETECTION_AGENTIC_EXECUTION_OUTCOME === 'failure'; + const msg = 'ERR_SYSTEM: \u274C Unexpected error loading threat detection module: ' + (loadErr && loadErr.message ? loadErr.message : String(loadErr)); + core.error(msg); + core.setOutput('reason', 'parse_error'); + if (continueOnError && !detectionExecutionFailed) { + core.warning('\u26A0\uFE0F ' + msg); + core.setOutput('conclusion', 'warning'); + core.setOutput('success', 'false'); + } else { + core.setOutput('conclusion', 'failure'); + core.setOutput('success', 'false'); + core.setFailed(msg); + } + } + + safe_outputs: + needs: + - activation + - agent + - detection + if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' + runs-on: ubuntu-slim + permissions: + contents: read + issues: write + timeout-minutes: 15 + env: + GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-repo-status" + GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }} + GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }} + GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }} + GH_AW_ENGINE_ID: "copilot" + GH_AW_ENGINE_MODEL: "claude-haiku-4.5" + GH_AW_ENGINE_VERSION: "1.0.52" + GH_AW_WORKFLOW_ID: "daily-repo-status" + GH_AW_WORKFLOW_NAME: "Repo Status" + GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/repo-status.md@main" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/main/workflows/repo-status.md" + outputs: + code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} + code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} + create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} + create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} + created_issue_number: ${{ steps.process_safe_outputs.outputs.created_issue_number }} + created_issue_url: ${{ steps.process_safe_outputs.outputs.created_issue_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} + steps: + - name: Setup Scripts + id: setup + uses: github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Repo Status" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-repo-status.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.52" + GH_AW_INFO_AWF_VERSION: "v0.25.55" + GH_AW_INFO_BODY_MODIFIED: "false" + GH_AW_INFO_ENGINE_ID: "copilot" + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + id: setup-agent-output-env + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + - name: Configure GH_HOST for enterprise compatibility + id: ghes-host-config + shell: bash + run: | + # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct + # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op. + GH_HOST="${GITHUB_SERVER_URL#https://}" + GH_HOST="${GH_HOST#http://}" + echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV" + - name: Process Safe Outputs + id: process_safe_outputs + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }} + GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_API_URL: ${{ github.api_url }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"close_older_issues\":true,\"labels\":[\"report\",\"daily-status\"],\"max\":1,\"title_prefix\":\"[repo-status] \"},\"create_report_incomplete_issue\":{},\"mentions\":{\"enabled\":false},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/safe_output_handler_manager.cjs'); + await main(); + - name: Upload Safe Outputs Items + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: safe-outputs-items + path: | + /tmp/gh-aw/safe-output-items.jsonl + /tmp/gh-aw/temporary-id-map.json + if-no-files-found: ignore diff --git a/.github/workflows/daily-repo-status.md b/.github/workflows/daily-repo-status.md new file mode 100644 index 000000000000..49b553940b8b --- /dev/null +++ b/.github/workflows/daily-repo-status.md @@ -0,0 +1,62 @@ +--- +description: | + This workflow creates daily repo status reports. It gathers recent repository + activity (issues, PRs, discussions, releases, code changes) and generates + engaging GitHub issues with productivity insights, community highlights, + and project recommendations. + +on: + schedule: daily + workflow_dispatch: + +permissions: + contents: read + issues: read + pull-requests: read + +network: defaults + +engine: + id: copilot + model: claude-haiku-4.5 + +tools: + github: + # If in a public repo, setting `lockdown: false` allows + # reading issues, pull requests and comments from 3rd-parties + # If in a private repo this has no particular effect. + lockdown: false + min-integrity: none # This workflow is allowed to examine and comment on any issues + +safe-outputs: + mentions: false + allowed-github-references: [] + create-issue: + title-prefix: "[repo-status] " + labels: [report, daily-status] + close-older-issues: true +source: githubnext/agentics/workflows/repo-status.md@main +--- + +# Repo Status + +Create an upbeat daily status report for the repo as a GitHub issue. + +## What to include + +- Recent repository activity (issues, PRs, discussions, releases, code changes) +- Progress tracking, goal reminders and highlights +- Project status and recommendations +- Actionable next steps for maintainers + +## Style + +- Be positive, encouraging, and helpful 🌟 +- Use emojis moderately for engagement +- Keep it concise - adjust length based on actual activity + +## Process + +1. Gather recent activity from the repository +2. Study the repository, its issues and its pull requests +3. Create a new GitHub issue with your findings and insights diff --git a/.github/workflows/docker-cloudstack-simulator.yml b/.github/workflows/docker-cloudstack-simulator.yml index 21a09d04e0b3..ac778a48af54 100644 --- a/.github/workflows/docker-cloudstack-simulator.yml +++ b/.github/workflows/docker-cloudstack-simulator.yml @@ -35,10 +35,10 @@ concurrency: jobs: build: if: github.repository == 'apache/cloudstack' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Login to Docker Registry - uses: docker/login-action@v2 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ${{ secrets.DOCKER_REGISTRY }} username: ${{ secrets.DOCKERHUB_USER }} @@ -47,7 +47,9 @@ jobs: - name: Set Docker repository name run: echo "DOCKER_REPOSITORY=apache" >> $GITHUB_ENV - - uses: actions/checkout@v4 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false - name: Set ACS version run: echo "ACS_VERSION=$(grep '' pom.xml | head -2 | tail -1 | cut -d'>' -f2 |cut -d'<' -f1)" >> $GITHUB_ENV diff --git a/.github/workflows/license-templates/LICENSE.txt b/.github/workflows/license-templates/LICENSE.txt new file mode 100644 index 000000000000..60b675e31016 --- /dev/null +++ b/.github/workflows/license-templates/LICENSE.txt @@ -0,0 +1,16 @@ +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/.github/workflows/main-sonar-check.yml b/.github/workflows/main-sonar-check.yml index dccd7174e54b..f48409fbc0c3 100644 --- a/.github/workflows/main-sonar-check.yml +++ b/.github/workflows/main-sonar-check.yml @@ -15,54 +15,53 @@ # specific language governing permissions and limitations # under the License. -name: Main Branch Sonar Quality Check - +name: Sonar Quality Check (Main) +permissions: + contents: read on: push: branches: - main - -permissions: - contents: read # to fetch code (actions/checkout) - pull-requests: write # for sonar to comment on pull-request - +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} jobs: build: if: github.repository == 'apache/cloudstack' - name: Main Sonar JaCoCo Build - runs-on: ubuntu-22.04 + name: Sonar JaCoCo Coverage + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 - - - name: Set up JDK17 - uses: actions/setup-java@v4 + persist-credentials: false + - name: Setup Environment + uses: ./.github/actions/setup-env with: - distribution: 'temurin' - java-version: '17' - cache: 'maven' - + install-python: 'true' + install-apt-deps: 'true' - name: Cache SonarCloud packages - uses: actions/cache@v4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - - - name: Cache local Maven repository - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-m2-${{ hashFiles('pom.xml', '*/pom.xml', '*/*/pom.xml', '*/*/*/pom.xml') }} - restore-keys: | - ${{ runner.os }}-m2 - - - name: Run Tests with Coverage + - name: Install Non-OSS + uses: ./.github/actions/install-nonoss + - name: Run Build and Tests with Coverage + run: mvn -B -T$(nproc) -P developer,systemvm,quality -Dsimulator -Dnoredist clean install + - name: Upload to SonarQube env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: | - git clone https://github.com/shapeblue/cloudstack-nonoss.git nonoss - cd nonoss && bash -x install-non-oss.sh && cd .. - mvn -T$(nproc) -P quality -Dsimulator -Dnoredist clean install org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=apache_cloudstack + run: mvn -B -P quality org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=apache_cloudstack -Dsonar.branch.name=${{ github.ref_name }} + - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 + with: + files: ./client/target/site/jacoco-aggregate/jacoco.xml + fail_ci_if_error: true + flags: unittests + verbose: true + name: codecov + token: ${{ secrets.CODECOV_TOKEN }} + - name: Compute Coverage Grade + run: bash scripts/coverage-grade.sh client/target/site/jacoco-aggregate/jacoco.xml diff --git a/.github/workflows/merge-conflict-checker.yml b/.github/workflows/merge-conflict-checker.yml index 860e1c1b5614..badf8c4b4c5b 100644 --- a/.github/workflows/merge-conflict-checker.yml +++ b/.github/workflows/merge-conflict-checker.yml @@ -17,28 +17,26 @@ name: "PR Merge Conflict Check" on: - push: - pull_request_target: - types: [synchronize] + schedule: + - cron: '*/10 * * * *' + workflow_dispatch: -permissions: # added using https://github.com/step-security/secure-workflows - contents: read +permissions: {} concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true + group: "gh-aw-${{ github.workflow }}" jobs: triage: permissions: - pull-requests: write # for eps1lon/actions-label-merge-conflict to label PRs - runs-on: ubuntu-22.04 + pull-requests: write # for eps1lon/actions-label-merge-conflict to label PRs + runs-on: ubuntu-24.04 steps: - - name: Conflict Check - uses: eps1lon/actions-label-merge-conflict@v2.0.0 - with: - repoToken: "${{ secrets.GITHUB_TOKEN }}" - dirtyLabel: "status:has-conflicts" - removeOnDirtyLabel: "status:ready-for-review" - continueOnMissingPermissions: true - commentOnDirty: "This pull request has merge conflicts. Dear author, please fix the conflicts and sync your branch with the base branch." + - name: Conflict Check + uses: eps1lon/actions-label-merge-conflict@0273be72a0bbd58fcd71d0d6c02c209b50d1e5e1 # v3.1.0 + with: + repoToken: "${{ secrets.GITHUB_TOKEN }}" + dirtyLabel: "status:has-conflicts" + removeOnDirtyLabel: "status:ready-for-review" + continueOnMissingPermissions: true + commentOnDirty: "This pull request has merge conflicts. Dear author, please fix the conflicts and sync your branch with the base branch." diff --git a/.github/workflows/linter.yml b/.github/workflows/pre-commit.yml similarity index 65% rename from .github/workflows/linter.yml rename to .github/workflows/pre-commit.yml index b6c814a36f4c..4de995479c59 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/pre-commit.yml @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. -name: Lint +name: pre-commit on: [pull_request] @@ -29,19 +29,27 @@ concurrency: jobs: pre-commit: name: Run pre-commit - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Check Out - uses: actions/checkout@v4 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.11' + cache: 'pip' - name: Install - run: | - python -m pip install --upgrade pip - pip install pre-commit + run: pip install pre-commit - name: Set PY run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV - - uses: actions/cache@v4 + - name: Cache pre-commit environments + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: ~/.cache/pre-commit key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }} - name: Run pre-commit - run: pre-commit run --all-files + run: pre-commit run --color=always --all-files + - name: Run manual pre-commit hooks + run: pre-commit run --color=always --all-files --hook-stage manual diff --git a/.github/workflows/rat.yml b/.github/workflows/rat.yml index 52ce343841ba..b75ff42b9122 100644 --- a/.github/workflows/rat.yml +++ b/.github/workflows/rat.yml @@ -16,32 +16,31 @@ # under the License. name: License Check - -on: [push, pull_request] - +on: + push: + branches: + - main + - 4.22 + - 4.20 + pull_request: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} permissions: contents: read - jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v4 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: - java-version: '17' - distribution: 'adopt' - architecture: x64 - cache: maven + persist-credentials: false + - name: Setup Environment + uses: ./.github/actions/setup-env + - name: Install Non-OSS + uses: ./.github/actions/install-nonoss - name: RAT licence checks run: | - git clone https://github.com/shapeblue/cloudstack-nonoss.git nonoss && cd nonoss && bash -x install-non-oss.sh && cd .. - rm -fr nonoss mvn -P developer,systemvm -Dsimulator -Dnoredist -pl . org.apache.rat:apache-rat-plugin:0.12:check - name: Rat Report if: always() diff --git a/.github/workflows/sonar-check.yml b/.github/workflows/sonar-check.yml index d31f55980a8f..fbb3cb9f540d 100644 --- a/.github/workflows/sonar-check.yml +++ b/.github/workflows/sonar-check.yml @@ -16,58 +16,100 @@ # under the License. name: Sonar Quality Check - -on: [pull_request] - permissions: - contents: read # to fetch code (actions/checkout) - pull-requests: write # for sonar to comment on pull-request - + contents: read + pull-requests: write +on: + pull_request: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} jobs: build: - if: github.repository == 'apache/cloudstack' && github.event.pull_request.head.repo.full_name == github.repository name: Sonar JaCoCo Coverage - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: - ref: "refs/pull/${{ github.event.number }}/merge" fetch-depth: 0 - - - name: Set up JDK17 - uses: actions/setup-java@v4 + persist-credentials: false + - name: Setup Environment + uses: ./.github/actions/setup-env with: - distribution: 'temurin' - java-version: '17' - cache: 'maven' - + install-python: 'true' + install-apt-deps: 'true' - name: Cache SonarCloud packages - uses: actions/cache@v4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - - - name: Cache local Maven repository - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-m2-${{ hashFiles('pom.xml', '*/pom.xml', '*/*/pom.xml', '*/*/*/pom.xml') }} - restore-keys: | - ${{ runner.os }}-m2 - + - name: Install Non-OSS + uses: ./.github/actions/install-nonoss - name: Run Build and Tests with Coverage - id: coverage + run: mvn -B -T$(nproc) -P developer,systemvm,quality -Dsimulator -Dnoredist clean install + - name: Upload to SonarQube + if: github.repository == 'apache/cloudstack' && github.event.pull_request.head.repo.full_name == github.repository env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} PR_ID: ${{ github.event.pull_request.number }} HEADREF: ${{ github.event.pull_request.head.ref }} run: | - git clone https://github.com/shapeblue/cloudstack-nonoss.git nonoss - cd nonoss && bash -x install-non-oss.sh && cd .. - mvn -T$(nproc) -P quality -Dsimulator -Dnoredist clean install org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=apache_cloudstack -Dsonar.pullrequest.key="$PR_ID" -Dsonar.pullrequest.branch="$HEADREF" -Dsonar.pullrequest.github.repository=apache/cloudstack -Dsonar.pullrequest.provider=GitHub -Dsonar.pullrequest.github.summary_comment=true + mvn -B -P quality org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=apache_cloudstack -Dsonar.pullrequest.key="$PR_ID" -Dsonar.pullrequest.branch="$HEADREF" -Dsonar.pullrequest.github.repository=apache/cloudstack -Dsonar.pullrequest.provider=GitHub -Dsonar.pullrequest.github.summary_comment=true + - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 + with: + files: ./client/target/site/jacoco-aggregate/jacoco.xml + fail_ci_if_error: true + flags: unittests + verbose: true + name: codecov + token: ${{ secrets.CODECOV_TOKEN }} + - name: Compute Coverage Grade + id: grade + run: bash scripts/coverage-grade.sh client/target/site/jacoco-aggregate/jacoco.xml + - name: Post Coverage Grade Comment on PR + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const grade = '${{ steps.grade.outputs.coverage_grade }}'; + const label = '${{ steps.grade.outputs.coverage_grade_label }}'; + const linePct = '${{ steps.grade.outputs.line_coverage }}'; + const branchPct = '${{ steps.grade.outputs.branch_coverage }}'; + const emojiMap = { A: '🟢', B: '🟡', C: '🟠', D: '🔴', F: '⛔' }; + const emoji = emojiMap[grade] ?? '❓'; + const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + + const branchRow = branchPct !== 'N/A' + ? `| Branch coverage | **${branchPct}%** |` + : ''; + + const body = [ + `## ${emoji} Test Coverage Grade: \`${grade}\` — ${label}`, + '', + '| Metric | Value |', + '|--------|-------|', + `| Line coverage | **${linePct}%** |`, + branchRow, + '', + '### Grade Scale', + '| Grade | Line Coverage | Meaning |', + '|-------|--------------|---------|', + '| 🟢 A | ≥ 80% | Excellent - this code sleeps well at night 😴 |', + '| 🟡 B | 60-79% | Good - almost there, don\'t stop now 😉 |', + '| 🟠 C | 40-59% | Acceptable - your code is wearing a seatbelt, but no airbags 😬 |', + '| 🔴 D | 20-39% | Marginal - boldly shipping where no test has gone before 🖖 |', + '| ⛔ F | < 20% | Failing - tests? what tests? 🔥 |', + '', + '> Branch coverage is shown as a secondary signal. Grade is determined by **line coverage**.', + `> [View full Actions run](${runUrl})`, + ].filter(l => l !== undefined).join('\n'); + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: body, + }); + console.log('Posted coverage grade comment'); diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000000..c0da5f98cc8e --- /dev/null +++ b/.github/workflows/stale.yml @@ -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. + +name: 'Close stale issues and PRs' +on: + schedule: + - cron: '30 1 * * *' + +jobs: + stale: + runs-on: ubuntu-latest + permissions: + actions: write + issues: write + pull-requests: write + steps: + - uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0 + with: + stale-issue-message: 'This issue is stale because it has been open for 120 days with no activity. It may be removed by administrators of this project at any time. Remove the stale label or comment to request for removal of it to prevent this.' + stale-pr-message: 'This PR is stale because it has been open for 120 days with no activity. It may be removed by administrators of this project at any time. Remove the stale label or comment to request for removal of it to prevent this.' + close-issue-message: 'This issue was closed because it has been stale for 120 days with no activity.' + close-pr-message: 'This PR was closed because it has been stale for 240 days with no activity.' + stale-issue-label: 'no-issue-activity' + stale-pr-label: 'no-pr-activity' + days-before-stale: 120 + days-before-close: -1 + days-before-pr-close: 240 + exempt-issue-labels: 'gsoc,good-first-issue,long-term-plan' + exempt-pr-labels: 'status:ready-for-merge,status:needs-testing,status:on-hold' + - uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0 + with: + stale-issue-label: 'archive' + days-before-stale: 240 + exempt-issue-labels: 'gsoc,good-first-issue,long-term-plan' + days-before-close: -1 diff --git a/.github/workflows/ui.yml b/.github/workflows/ui.yml index 88d4a70e4c24..7f23add660f0 100644 --- a/.github/workflows/ui.yml +++ b/.github/workflows/ui.yml @@ -17,7 +17,13 @@ name: UI Build -on: [push, pull_request] +on: + push: + branches: + - main + - 4.22 + - 4.20 + pull_request: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -28,15 +34,19 @@ permissions: jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false - name: Set up Node - uses: actions/setup-node@v3 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 16 + cache: 'npm' + cache-dependency-path: 'ui/package-lock.json' - name: Env details run: | @@ -55,7 +65,7 @@ jobs: npm run lint npm run test:unit - - uses: codecov/codecov-action@v4 + - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 if: github.repository == 'apache/cloudstack' with: working-directory: ui diff --git a/.markdownlintignore b/.markdownlintignore new file mode 100644 index 000000000000..9fc9e21de772 --- /dev/null +++ b/.markdownlintignore @@ -0,0 +1 @@ +CHANGES.md diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5c79d1dd0021..91537e25267e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,19 +25,112 @@ repos: hooks: - id: identity - id: check-hooks-apply + - repo: https://github.com/thlorenz/doctoc.git + rev: v2.2.0 + hooks: + - id: doctoc + name: Add TOC for Markdown files + files: ^CONTRIBUTING\.md$|^INSTALL\.md$|^README\.md$ + - repo: https://github.com/oxipng/oxipng + rev: v9.1.5 + hooks: + - id: oxipng + name: run oxipng + description: optimize PNG images with lossless compression + args: ['-o', '4', '--strip', 'safe', '--alpha'] - repo: https://github.com/gitleaks/gitleaks rev: v8.27.2 hooks: - id: gitleaks name: run gitleaks description: detect hardcoded secrets + - repo: https://github.com/Lucas-C/pre-commit-hooks + rev: v1.5.5 + hooks: + - id: chmod + name: set file permissions + args: ['644'] + files: \.md$ + stages: [manual] + - id: insert-license + name: add license for all cfg files + description: automatically adds a licence header to all cfg files that don't have a license header + files: \.cfg$ + args: + - --comment-style + - '|#|' + - --license-filepath + - .github/workflows/license-templates/LICENSE.txt + - --fuzzy-match-generates-todo + - id: insert-license + name: add license for all Markdown files + files: \.md$ + args: + - --comment-style + - '' + - --license-filepath + - .github/workflows/license-templates/LICENSE.txt + - --fuzzy-match-generates-todo + exclude: ^(CHANGES|ISSUE_TEMPLATE|PULL_REQUEST_TEMPLATE)\.md$|^ui/docs/(full|smoke)-test-plan\.template\.md$|^\.github/workflows/.*\.md$|^\.github/aw/.*\.md$ + - id: insert-license + name: add license for all properties files + description: automatically adds a licence header to all properties files that don't have a license header + files: \.properties$ + args: + - --comment-style + - '|#|' + - --license-filepath + - .github/workflows/license-templates/LICENSE.txt + - --fuzzy-match-generates-todo + - id: insert-license + name: add license for all Shell files + description: automatically adds a licence header to all Shell files that don't have a license header + files: \.sh$ + args: + - --comment-style + - '|#|' + - --license-filepath + - .github/workflows/license-templates/LICENSE.txt + - --fuzzy-match-generates-todo + - id: insert-license + name: add license for all SQL files + files: \.sql$ + args: + - --comment-style + - '|--|' + - --license-filepath + - .github/workflows/license-templates/LICENSE.txt + - --fuzzy-match-generates-todo + - id: insert-license + name: add license for all Vue files + files: \.vue$ + args: + - --comment-style + - '|//|' + - --license-filepath + - .github/workflows/license-templates/LICENSE.txt + - --fuzzy-match-generates-todo + - id: insert-license + name: add license for all YAML files + description: automatically adds a licence header to all YAML files that don't have a license header + files: \.ya?ml$ + args: + - --comment-style + - '|#|' + - --license-filepath + - .github/workflows/license-templates/LICENSE.txt + - --fuzzy-match-generates-todo + exclude: ^\.github/workflows/.*\.lock\.yml$ - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: #- id: check-added-large-files - id: check-case-conflict #- id: check-executables-have-shebangs + - id: check-illegal-windows-names - id: check-merge-conflict + - id: check-shebang-scripts-are-executable + files: \.sh$ - id: check-symlinks - id: check-vcs-permalinks #- id: check-yaml @@ -48,7 +141,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/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$| @@ -58,42 +151,31 @@ 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$| - ^test/integration/smoke/test_ssl_offloading.py$ + ^systemvm/agent/certs/systemvm\.key$| + ^test/integration/smoke/test_ssl_offloading\.py$ - id: end-of-file-fixer - exclude: \.vhd$ + exclude: \.vhd$|\.svg$ + - id: file-contents-sorter + args: [--unique] + files: ^\.github/linters/codespell\.txt$ - id: fix-byte-order-marker - id: forbid-submodules - 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: ^(LICENSE|NOTICE)$|README$|\.(bat|cfg|config|cs|css|erb|gitignore|header|in|install|java|md|properties|py|rb|rc|sh|sql|svg|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 - rev: v2.2.6 + rev: v2.4.2 hooks: - id: codespell name: run codespell description: Check spelling with codespell - args: [--ignore-words=.github/linters/codespell.txt] - 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.45.0 hooks: @@ -102,4 +184,14 @@ repos: description: check Markdown files with markdownlint args: [--config=.github/linters/.markdown-lint.yml] types: [markdown] - files: \.(md|mdown|markdown)$ + files: \.md$ + - repo: https://github.com/adrienverge/yamllint + rev: v1.37.1 + hooks: + - id: yamllint + name: run yamllint + description: check YAML files with yamllint + args: [--config-file=.github/linters/.yamllint.yml] + types: [yaml] + files: \.ya?ml$ + exclude: ^.*k8s-.*\.ya?ml$|^.github/workflows/.*\.lock\.ya?ml$ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000000..4469efa2f494 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,32 @@ + + +# Agent Guide for Apache CloudStack + +This file is read by automated agents (security scanners, code +analyzers, AI assistants) operating on this repository. + +## Security + +Security model: [SECURITY.md](./SECURITY.md) + +Agents that scan this repository should consult `SECURITY.md` and the +threat model it links before reporting issues. + +The project-wide security threat model is linked from `SECURITY.md`. diff --git a/CHANGES.md b/CHANGES.md index 8fea4f3a355b..7a670a55a61f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -644,12 +644,12 @@ Bug ID | Description Version 4.2.1 ------------- -Release notes contain the list of [bug fixes](http://cloudstack.apache.org/docs/en-US/Apache_CloudStack/4.2.1/html/Release_Notes/version-4.2.html#issues-fixed-4.2.1) +Release notes contain the list of [bug fixes](https://cloudstack.apache.org/docs/en-US/Apache_CloudStack/4.2.1/html/Release_Notes/version-4.2.html#issues-fixed-4.2.1) Version 4.2.0 ------------- Released on October 1 2013. -Release notes contain the list of [bug fixes](http://cloudstack.apache.org/docs/en-US/Apache_CloudStack/4.2.0/html/Release_Notes/index.html) +Release notes contain the list of [bug fixes](https://cloudstack.apache.org/docs/en-US/Apache_CloudStack/4.2.0/html/Release_Notes/index.html) Version 4.1.0 ------------- diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 17ff4badc97c..f0678ed76498 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,31 +1,65 @@ -Contributing to Apache CloudStack (ACS) -======================================= + + +# Contributing to Apache CloudStack (ACS) + +## Summary + + + + +- [Summary](#summary) +- [Bug fixes](#bug-fixes) +- [Developing new features](#developing-new-features) +- [PendingReleaseNotes file](#pendingreleasenotes-file) +- [Fork the code](#fork-the-code) +- [Making changes](#making-changes) +- [Rebase `feature_x` to include updates from `upstream/main`](#rebase-feature_x-to-include-updates-from-upstreammain) +- [Make a GitHub Pull Request to contribute your changes](#make-a-github-pull-request-to-contribute-your-changes) +- [Cleaning up after a successful pull request](#cleaning-up-after-a-successful-pull-request) +- [Release Principles](#release-principles) + + + +## Summary -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. _Let's get started!!!_ -Bug fixes ---------- +## Bug fixes It's very important that we can easily track bug fix commits, so their hashes should remain the same in all branches. Therefore, a pull request (PR) that fixes a bug, should be sent against a release branch. This can be either the "current release" or the "previous release", depending on which ones are maintained. Since the goal is a stable main, bug fixes should be "merged forward" to the next branch in order: "previous release" -> "current release" -> main (in other words: old to new) -Developing new features ------------------------ +## Developing new features Development should be done in a feature branch, branched off of main. Send a PR(steps below) to get it into main (2x LGTM applies). PR will only be merged when main is open, will be held otherwise until main is open again. No back porting / cherry-picking features to existing branches! -PendingReleaseNotes file ------------------------- +## PendingReleaseNotes file + 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. @@ -33,8 +67,7 @@ When adding information to the PendingReleaseNotes file make sure that you write Updating the PendingReleaseNotes file is preferably a part of the original Pull Request, but that is up to the developers' discretion. -Fork the code -------------- +## Fork the code In your browser, navigate to: [https://github.com/apache/cloudstack](https://github.com/apache/cloudstack) @@ -51,8 +84,7 @@ $ git fetch upstream $ git rebase upstream/main ``` -Making changes --------------- +## Making changes It is important that you create a new branch to make changes on and that you do not change the `main` branch (other than to rebase in changes from `upstream/main`). In this example I will assume you will be making your changes to a branch called `feature_x`. This `feature_x` branch will be created on your local repository and will be pushed to your forked repository on GitHub. Once this branch is on your fork you will create a Pull Request for the changes to be added to the ACS project. @@ -68,8 +100,7 @@ $ git commit -a -m "descriptive commit message for your changes" > The `-b` specifies that you want to create a new branch called `feature_x`. You only specify `-b` the first time you checkout because you are creating a new branch. Once the `feature_x` branch exists, you can later switch to it with only `git checkout feature_x`. -Rebase `feature_x` to include updates from `upstream/main` ------------------------------------------------------------- +## Rebase `feature_x` to include updates from `upstream/main` It is important that you maintain an up-to-date `main` branch in your local repository. This is done by rebasing in the code changes from `upstream/main` (the official ACS project repository) into your local repository. You will want to do this before you start working on a feature as well as right before you submit your changes as a pull request. I recommend you do this process periodically while you work to make sure you are working off the most recent project code. @@ -89,8 +120,7 @@ $ git rebase main > Now your `feature_x` branch is up-to-date with all the code in `upstream/main`. -Make a GitHub Pull Request to contribute your changes ------------------------------------------------------ +## 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. @@ -114,8 +144,7 @@ To initiate the pull request, do the following: If you are requested to make modifications to your proposed changes, make the changes locally on your `feature_x` branch, re-push the `feature_x` branch to your fork. The existing pull request should automatically pick up the change and update accordingly. -Cleaning up after a successful pull request -------------------------------------------- +## Cleaning up after a successful pull request Once the `feature_x` branch has been committed into the `upstream/main` branch, your local `feature_x` branch and the `origin/feature_x` branch are no longer needed. If you want to make additional changes, restart the process with a new branch. @@ -129,6 +158,6 @@ $ git branch -D feature_x $ git push origin :feature_x ``` -Release Principles ------------------- +## Release Principles + Detailed information about ACS release principles is available at https://cwiki.apache.org/confluence/display/CLOUDSTACK/Release+principles+for+Apache+CloudStack+4.6+and+up diff --git a/INSTALL.md b/INSTALL.md index efb42678b0a3..52f109b0a411 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,15 +1,46 @@ + + # Apache CloudStack Installation basics This document describes how to develop, build, package and install Apache -CloudStack. For more information please refer to the official [documentation](http://docs.cloudstack.apache.org) +CloudStack. For more information please refer to the official [documentation](https://docs.cloudstack.apache.org) or the developer [wiki](https://cwiki.apache.org/confluence/display/CLOUDSTACK/Home). Apache CloudStack developers use various platforms for development, this guide was tested against a CentOS 7 x86_64 setup. -* [Setting up development environment](https://cwiki.apache.org/confluence/display/CLOUDSTACK/Setting+up+CloudStack+Development+Environment) for Apache CloudStack. -* [Building](https://cwiki.apache.org/confluence/display/CLOUDSTACK/How+to+build+CloudStack) Apache CloudStack. -* [Appliance based development](https://github.com/rhtyd/monkeybox) + + + +- [Setting up Development Environment](#setting-up-development-environment) + - [Using jenv and/or pyenv for Version Management](#using-jenv-andor-pyenv-for-version-management) +- [Getting the Source Code](#getting-the-source-code) +- [Building](#building) +- [To bring up CloudStack UI](#to-bring-up-cloudstack-ui) +- [Building with non-redistributable plugins](#building-with-non-redistributable-plugins) +- [Packaging and Installation](#packaging-and-installation) + - [Debian/Ubuntu](#debianubuntu) + - [RHEL/CentOS](#rhelcentos) +- [Notes](#notes) + + ## Setting up Development Environment @@ -41,7 +72,7 @@ Start the MySQL service: ### Using jenv and/or pyenv for Version Management -CloudStack is built using Java and Python. To make selection of these tools versions more consistent and ease installation for developers, optional support for [jenv](http://www.jenv.be/) and [pyenv](https://github.com/yyuu/pyenv) with [virtualenv]|(https://github.com/yyuu/pyenv-virtualenv) is provided. jenv installation instructions are available here and pyenv installation instructions are available here. For users of [oh-my-zsh](http://ohmyz.sh/) there is a pyenv plugin available to trigger configuration of pyenv in a shell session. +CloudStack is built using Java and Python. To make selection of these tools versions more consistent and ease installation for developers, optional support for [jenv](http://www.jenv.be/) and [pyenv](https://github.com/yyuu/pyenv) with [virtualenv]|(https://github.com/yyuu/pyenv-virtualenv) is provided. jenv installation instructions are available here and pyenv installation instructions are available here. For users of [oh-my-zsh](https://ohmyz.sh/) there is a pyenv plugin available to trigger configuration of pyenv in a shell session. Following installation, execute the following commands to configure jenv and pyenv for use with CloudStack development: @@ -171,7 +202,7 @@ All the rpm packages will be created in `dist/rpmbuild/RPMS/x86_64` directory. ## Notes -If you will be using Xen as your hypervisor, please download [vhd-util](http://download.cloudstack.org/tools/vhd-util) +If you will be using Xen as your hypervisor, please download [vhd-util](https://download.cloudstack.org/tools/vhd-util) If management server is installed on RHEL/CentOS, then copy vhd-util into: diff --git a/LICENSE b/LICENSE index 8be7d8083a5e..e61c431f5fad 100644 --- a/LICENSE +++ b/LICENSE @@ -177,14 +177,14 @@ Copyright (c) 2014 The Apache Software Foundation of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS - + This distribution contains third party resources. Within the console-proxy/js directory licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) - Copyright (c) 2009, John Resig - + Copyright (c) 2009, John Resig + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including @@ -192,10 +192,10 @@ Within the console-proxy/js directory distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -203,43 +203,43 @@ Within the console-proxy/js directory LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - from John Resig - jquery.js + + from John Resig + jquery.js Within the systemvm/debian/etc directory placed in the public domain - by Adiscon GmbH http://www.adiscon.com/ - rsyslog.conf - by Simon Kelley - dnsmasq.conf - vpcdnsmasq.conf + by Adiscon GmbH http://www.adiscon.com/ + rsyslog.conf + by Simon Kelley + dnsmasq.conf + vpcdnsmasq.conf Within the systemvm/debian/etc/apache2 directory licensed under the Apache License, Version 2 http://www.apache.org/licenses/LICENSE-2.0.txt (as above) Copyright (c) 2012 The Apache Software Foundation - from The Apache Software Foundation http://www.apache.org/ - httpd.conf + from The Apache Software Foundation http://www.apache.org/ + httpd.conf vhost.template Within the systemvm/debian/etc/ssh/ directory licensed under the BSD (2-clause) http://www.opensource.org/licenses/BSD-2-Clause (as follows) - - + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - + Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -250,55 +250,55 @@ Within the systemvm/debian/etc/ssh/ directory ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - from OpenSSH Project http://www.openssh.org/ - sshd_config + + from OpenSSH Project http://www.openssh.org/ + sshd_config Within the systemvm/debian/root/redundant_router directory placed in the public domain - by The netfilter.org project http://www.netfilter.org/ - conntrackd.conf.templ + by The netfilter.org project http://www.netfilter.org/ + conntrackd.conf.templ Within the scripts/storage/secondary directory licensed under the Apache License, Version 2 http://www.apache.org/licenses/LICENSE-2.0.txt (as above) Copyright (c) 2010-2011 OpenStack, LLC. - from OpenStack, LLC http://www.openstack.org - swift + from OpenStack, LLC http://www.openstack.org + swift Within the scripts/vm/hypervisor/xenserver directory licensed under the Apache License, Version 2 http://www.apache.org/licenses/LICENSE-2.0.txt (as above) Copyright (c) 2010-2011 OpenStack, LLC. - from OpenStack, LLC http://www.openstack.org - swift + from OpenStack, LLC http://www.openstack.org + swift Within the ui/lib directory placed in the public domain - by Eric Meyer http://meyerweb.com/eric/ + by Eric Meyer http://meyerweb.com/eric/ reset.css from http://meyerweb.com/eric/tools/css/reset/ licensed under the Apache License, Version 2 http://www.apache.org/licenses/LICENSE-2.0.txt (as above) Copyright (c) 2006 Google Inc. - from Google Inc. http://google.com + from Google Inc. http://google.com excanvas.js from http://code.google.com/p/explorercanvas/ licensed under the BSD (2-clause) http://www.opensource.org/licenses/BSD-2-Clause (as follows) Copyright (c) 2008 George McGinley Smith - All rights reserved. - + All rights reserved. + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - + Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -309,13 +309,13 @@ Within the ui/lib directory ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - from George McGinley Smith - jquery.easing.js + + from George McGinley Smith + jquery.easing.js licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) - - + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including @@ -323,10 +323,10 @@ Within the ui/lib directory distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -334,14 +334,14 @@ Within the ui/lib directory LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - from The Dojo Foundation http://dojofoundation.org/ + + from The Dojo Foundation http://dojofoundation.org/ require.js from http://github.com/jrburke/requirejs licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) - Copyright (c) 2011, John Resig - + Copyright (c) 2011, John Resig + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including @@ -349,10 +349,10 @@ Within the ui/lib directory distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -360,14 +360,14 @@ Within the ui/lib directory LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - from John Resig - jquery.js + + from John Resig + jquery.js licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) Copyright (c) 2014 Jörn Zaefferer - + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including @@ -375,10 +375,10 @@ Within the ui/lib directory distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -386,9 +386,9 @@ Within the ui/lib directory LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - from Jorn Zaefferer - jquery.validate.js + + from Jorn Zaefferer + jquery.validate.js licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) @@ -418,8 +418,8 @@ Within the ui/lib directory licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) - Copyright (c) 2010, Sebastian Tschan - + Copyright (c) 2010, Sebastian Tschan + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including @@ -427,10 +427,10 @@ Within the ui/lib directory distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -438,14 +438,14 @@ Within the ui/lib directory LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - from Sebastian Tschan https://blueimp.net - jquery.md5.js + + from Sebastian Tschan https://blueimp.net + jquery.md5.js licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) - Copyright (c) 2006 Klaus Hartl (stilbuero.de) - + Copyright (c) 2006 Klaus Hartl (stilbuero.de) + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including @@ -453,10 +453,10 @@ Within the ui/lib directory distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -464,15 +464,15 @@ Within the ui/lib directory LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - from Klaus Hartl http://stilbuero.de - jquery.cookies.js + + from Klaus Hartl http://stilbuero.de + jquery.cookies.js Within the ui/lib/flot directory licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) - Released under the MIT license by IOLA, December 2007. - + Released under the MIT license by IOLA, December 2007. + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including @@ -480,10 +480,10 @@ Within the ui/lib/flot directory distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -491,24 +491,24 @@ Within the ui/lib/flot directory LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - from IOLA http://www.iola.dk/ - jquery.flot.crosshair.js - jquery.flot.fillbetween.js - jquery.flot.image.js - jquery.flot.js - jquery.flot.navigate.js - jquery.flot.resize.js - jquery.flot.selection.js - jquery.flot.stack.js - jquery.flot.symbol.js - jquery.flot.threshold.js + + from IOLA http://www.iola.dk/ + jquery.flot.crosshair.js + jquery.flot.fillbetween.js + jquery.flot.image.js + jquery.flot.js + jquery.flot.navigate.js + jquery.flot.resize.js + jquery.flot.selection.js + jquery.flot.stack.js + jquery.flot.symbol.js + jquery.flot.threshold.js licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) Created by Brian Medendorp, June 2009 - Updated November 2009 with contributions from: btburnett3, Anthony Aragues and Xavi Ivars - + Updated November 2009 with contributions from: btburnett3, Anthony Aragues and Xavi Ivars + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including @@ -516,10 +516,10 @@ Within the ui/lib/flot directory distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -527,13 +527,13 @@ Within the ui/lib/flot directory LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - from Brian Medendorp - jquery.pie.js + + from Brian Medendorp + jquery.pie.js licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) - - + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including @@ -541,10 +541,10 @@ Within the ui/lib/flot directory distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -552,14 +552,14 @@ Within the ui/lib/flot directory LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - from Ole Laursen - jquery.colorhelpers.js + + from Ole Laursen + jquery.colorhelpers.js Within the ui/lib/jquery-ui directory licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) - - + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including @@ -567,10 +567,10 @@ Within the ui/lib/jquery-ui directory distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -578,17 +578,17 @@ Within the ui/lib/jquery-ui directory LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - from jQuery UI Developers http://jqueryui.com/about - css/jquery-ui.css - index.html - js/jquery-ui.js + + from jQuery UI Developers http://jqueryui.com/about + css/jquery-ui.css + index.html + js/jquery-ui.js Within the ui/lib/qunit directory licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) - Copyright (c) 2012 John Resig, Jörn Zaefferer - + Copyright (c) 2012 John Resig, Jörn Zaefferer + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including @@ -596,10 +596,10 @@ Within the ui/lib/qunit directory distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -607,20 +607,20 @@ Within the ui/lib/qunit directory LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - from Jorn Zaefferer + + from Jorn Zaefferer qunit.css from http://docs.jquery.com/QUnit qunit.js from http://docs.jquery.com/QUnit Within the utils/src/main/java/com/cloud/utils/db directory licensed under the Apache License, Version 2 http://www.apache.org/licenses/LICENSE-2.0.txt (as above) Copyright (c) 2004 Clinton Begin - from Clinton Begin http://code.google.com/p/mybatis/ + from Clinton Begin http://code.google.com/p/mybatis/ ScriptRunner.java from http://code.google.com/p/mybatis/ Within the utils/src/main/java/org/apache/commons/httpclient/contrib/ssl directory licensed under the Apache License, Version 2 http://www.apache.org/licenses/LICENSE-2.0.txt (as above) Copyright (c) 2007 The Apache Software Foundation - from The Apache Software Foundation http://www.apache.org/ - EasySSLProtocolSocketFactory.java - EasyX509TrustManager.java + from The Apache Software Foundation http://www.apache.org/ + EasySSLProtocolSocketFactory.java + EasyX509TrustManager.java diff --git a/NOTICE b/NOTICE index b19e4a428530..8666be264b51 100644 --- a/NOTICE +++ b/NOTICE @@ -1,62 +1,62 @@ Apache CloudStack Copyright 2014 The Apache Software Foundation - + This product includes software developed at The Apache Software Foundation (http://www.apache.org/). - - + + This distribution contains third party resources requiring the following notices: - - For + + For jquery.js - + jQuery JavaScript Library v1.3.2 http://jquery.com/ - + Copyright (c) 2009 John Resig Dual licensed under the MIT and GPL licenses. http://docs.jquery.com/License - + Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) Revision: 6246 - - For + + For jquery.js - + jQuery JavaScript Library v1.6.4 http://jquery.com/ - + Copyright 2011, John Resig Dual licensed under the MIT or GPL Version 2 licenses. http://jquery.org/license - + Includes Sizzle.js http://sizzlejs.com/ Copyright 2011, The Dojo Foundation Released under the MIT, BSD, and GPL Licenses. - + Date: Mon Sep 12 18:54:48 2011 -0400 - - For + + For jquery.md5.js - + jQuery MD5 Plugin 1.2.1 https://github.com/blueimp/jQuery-MD5 - + Copyright 2010, Sebastian Tschan https://blueimp.net - + Licensed under the MIT license: http://creativecommons.org/licenses/MIT/ - + Based on A JavaScript implementation of the RSA Data Security, Inc. MD5 Message Digest Algorithm, as defined in RFC 1321. @@ -65,15 +65,15 @@ Copyright 2014 The Apache Software Foundation Distributed under the BSD License See http://pajhome.org.uk/crypt/md5 for more info. - - For + + For jquery.colorhelpers.js - + Plugin for jQuery for working with colors. - + Version 1.1. - + Inspiration from jQuery color animation plugin by John Resig. - + Released under the MIT license by Ole Laursen, October 2009. diff --git a/PRE_COMMIT.md b/PRE_COMMIT.md new file mode 100644 index 000000000000..62dc296c99e4 --- /dev/null +++ b/PRE_COMMIT.md @@ -0,0 +1,62 @@ + + +# pre-commit + +We run [pre-commit](https://pre-commit.com/) with +[GitHub Actions](https://github.com/apache/cloudstack/blob/main/.github/workflows/pre-commit.yml) so installation on your +local machine is currently optional. + +The `pre-commit` [configuration file](https://github.com/apache/cloudstack/blob/main/.pre-commit-config.yaml) +is in the repository root. Before you can run the hooks, you need to have `pre-commit` installed. `pre-commit` is a +[Python package](https://pypi.org/project/pre-commit/). + +From the repository root run: `pip install -r requirements-dev.txt` to install `pre-commit` and after you install +`pre-commit` you will then need to install the pre-commit hooks by running `pre-commit install`. + +The hooks run when running `git commit` and also from the command line with `pre-commit`. Some of the hooks will auto +fix the code after the hooks fail whilst most will print error messages from the linters. If a hook fails the overall +commit will fail, and you will need to fix the issues or problems and `git add` and `git commit` again. On `git commit` +the hooks will run mostly only against modified files so if you want to test all hooks against all files and when you +are adding a new hook you should always run: + +`pre-commit run --all-files` + +Sometimes you might need to skip a hook to commit because the hook is stopping you from committing or your computer +might not have all the installation requirements for all the hooks. The `SKIP` variable is comma separated for two or +more hooks: + +`SKIP=codespell git commit -m "foo"` + +The same applies when running pre-commit: + +`SKIP=codespell pre-commit run --all-files` + +Occasionally you can have more serious problems when using `pre-commit` with `git commit`. You can use `--no-verify` to +commit and stop `pre-commit` from checking the hooks. For example: + +`git commit --no-verify -m "foo"` + +If you are having major problems using `pre-commit` you can always uninstall it. + +To run a single hook use `pre-commit run --all-files ` + +For example just run the `codespell` hook: + +`pre-commit run --all-files codespell` diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 0bd7a7643ce1..be04436d3ff8 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -22,8 +22,8 @@ This PR... - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] Enhancement (improves an existing feature and functionality) - [ ] Cleanup (Code refactoring and cleanup, that may add test cases) -- [ ] build/CI -- [ ] test (unit or integration test code) +- [ ] Build/CI +- [ ] Test (unit or integration test code) ### Feature/Enhancement Scale or Bug Severity diff --git a/README.md b/README.md index 28d7d7396f41..852674ab7b1f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,54 @@ -# 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/) + + + +- [Who Uses CloudStack?](#who-uses-cloudstack) +- [Demo](#demo) +- [Getting Started](#getting-started) +- [Getting Source Repository](#getting-source-repository) +- [Documentation](#documentation) +- [News and Events](#news-and-events) +- [Getting Involved and Contributing](#getting-involved-and-contributing) +- [Reporting Security Vulnerabilities](#reporting-security-vulnerabilities) +- [License](#license) +- [Notice of Cryptographic Software](#notice-of-cryptographic-software) +- [Star History](#star-history) +- [Contributors](#contributors) + + + Apache CloudStack is open source software designed to deploy and manage large networks of virtual machines, as a highly available, highly scalable Infrastructure as a Service (IaaS) cloud computing platform. CloudStack is used @@ -21,7 +68,7 @@ OVM and LXC containers. Users can manage their cloud with an easy to use Web interface, command line tools, and/or a full-featured query based API. -For more information on Apache CloudStack, please visit the [website](http://cloudstack.apache.org) +For more information on Apache CloudStack, please visit the [website](https://cloudstack.apache.org) ## Who Uses CloudStack? @@ -78,10 +125,10 @@ via GitHub pull requests. ## Getting Involved and Contributing Interested in helping out with Apache CloudStack? Great! We welcome -participation from anybody willing to work [The Apache Way](http://theapacheway.com) and make a +participation from anybody willing to work [The Apache Way](https://theapacheway.com) and make a contribution. Note that you do not have to be a developer in order to contribute to Apache CloudStack. We need folks to help with documentation, translation, -promotion etc. See our contribution [page](http://cloudstack.apache.org/contribute.html). +promotion etc. See our contribution [page](https://cloudstack.apache.org/contribute.html). If you are a frequent contributors, you can request to be added as collaborators (see https://cwiki.apache.org/confluence/display/INFRA/Git+-+.asf.yaml+features#Git.asf.yamlfeatures-AssigningexternalcollaboratorswiththetriageroleonGitHub) @@ -92,7 +139,7 @@ You may do so by sharing your GitHub users ID or raise a GitHub issue. If you're interested in learning more or participating in the Apache CloudStack project, the mailing lists are the best way to do that. While the project has -several communications channels, the [mailing lists](http://cloudstack.apache.org/mailing-lists.html) are the most active and the +several communications channels, the [mailing lists](https://cloudstack.apache.org/mailing-lists.html) are the most active and the official channels for making decisions about the project itself. Mailing lists: @@ -112,7 +159,7 @@ released version of CloudStack, please report it to `security@apache.org` with details about the vulnerability, how it might be exploited, and any additional information that might be useful. -For more details, please visit our security [page](http://cloudstack.apache.org/security.html). +For more details, please visit our security [page](https://cloudstack.apache.org/security.html). ## License @@ -156,7 +203,7 @@ Unrestricted (TSU) exception (see the BIS Export Administration Regulations, Sec The following provides more details on the included cryptographic software: * CloudStack makes use of JaSypt cryptographic libraries. -* CloudStack has a system requirement of MySQL, and uses native database encryption functionality. +* CloudStack requires a MySQL-compatible database (MariaDB or MySQL), and uses native database encryption functionality. * 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. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000000..018f2fa8cb86 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,34 @@ + + +# Security Policy + +## Reporting a Vulnerability + +`apache/cloudstack` follows the [Apache Software Foundation security process](https://www.apache.org/security/). Please report suspected +vulnerabilities privately to `security@apache.org`; do not open public GitHub issues or pull requests for security reports. + +For more details, see https://cloudstack.apache.org/security.html. + +## Threat Model + +What the project treats as in scope and out of scope, the security +properties it provides and disclaims, the adversary model, and how +findings are triaged are documented in the project-wide threat model: +[THREAT_MODEL.md](THREAT_MODEL.md). diff --git a/THREAT_MODEL.md b/THREAT_MODEL.md new file mode 100644 index 000000000000..4659b937dedb --- /dev/null +++ b/THREAT_MODEL.md @@ -0,0 +1,1148 @@ + + +# Apache CloudStack Security Threat Model (draft) + +> **Document scope and PMC structural decision.** The CloudStack PMC owns +> five repositories: `apache/cloudstack` (the management server, agent, and +> systemvm), plus four satellite clients — `apache/cloudstack-cloudmonkey` +> (CLI), `apache/cloudstack-go` (Go SDK), `apache/cloudstack-terraform-provider`, +> `apache/cloudstack-kubernetes-provider`. This document models +> `apache/cloudstack` as the canonical threat model; the four satellite +> models are short *deltas* that inherit §3 / §4 / §7 from this +> document and add only what each satellite uniquely introduces (`§4 B1` +> reachability, the credential file shape, the wrapper-of-SDK contract, +> etc.). The deltas live at `/tmp/claude/cloudstack--threat-model-draft.md`. +> The satellite clients' interfaces point **inward** at the management-server +> API; some satellites additionally expose **outward** interfaces that are +> designed to be safe to expose *(maintainer: DaanHoogland)*. +> An umbrella model was rejected because the satellites are uniformly thin +> "HMAC-SHA1-signing HTTP client" wrappers — a single document either +> drowns them in CloudStack-server content or, worse, drowns the +> CloudStack-server content in satellite caveats. Each satellite is small +> enough that a 1–2 page delta works. + +## §1 Header + +- **Project:** Apache CloudStack (`apache/cloudstack`) — IaaS orchestration + platform. This document does **not** cover the four satellite repos, which + carry their own delta models. +- **Commit:** `7308dad1` (HEAD of `main` at draft time). +- **Date:** 2026-05-29. +- **Authors:** ASF Security team draft, awaiting CloudStack PMC review. +- **Status:** Draft — under maintainer review. +- **Version binding:** This document describes the model as of the commit + above. A vulnerability report against CloudStack release *N* (currently + the 4.20.x line) should be triaged against the model as it stood at *N*'s + release tag, not against HEAD. +- **Reporting:** vulnerabilities that fall under §8 (claimed properties) + should be reported per the project's published policy + (`security@apache.org` per `README.md` and + `https://cloudstack.apache.org/security.html`); reports that fall under + §3 (out of scope), §9 (properties not provided), or §11a (known + non-findings) will be closed by CloudStack triagers citing this document. +- **Provenance legend** — + *(documented)* = paraphrased from an in-repo source or the project website + with citation; *(maintainer)* = stated by a CloudStack PMC member in + response to this draft; *(inferred)* = synthesized by the producer from + code structure or domain knowledge, awaiting PMC ratification (every + *(inferred)* tag has a matching §14 question). +- **Draft confidence (provenance-tag tally):** 51 *(documented)* / 42 + *(maintainer)* / 38 *(inferred)*. Eleven formerly-open questions (Q1, + Q2, Q4, Q5, Q12 — including the highest-leverage Root-CA strictness + default — plus Q8, Q9, Q10, Q17, Q18, Q19 from the 2026-06-08 review) + were resolved by the CloudStack PMC review (DaanHoogland, vishesh92) and + their tags promoted from *(inferred)* to *(maintainer)*. + +**About the project.** Apache CloudStack is an open-source Infrastructure-as-a- +Service (IaaS) orchestration platform *(documented: `README.md`, +`https://cloudstack.apache.org/`)*. It deploys and manages large fleets of +virtual machines across multiple hypervisors (KVM, VMware, XenServer/XCP-ng, +Hyper-V, baremetal-bridge, OVM) and over object/block/file storage +(NFS, Ceph/RBD, iSCSI, SMB, primary-storage plugins, S3-compatible secondary +storage). A central **management server** (Java/Tomcat-style servlets, +backed by MariaDB/MySQL) exposes a signed REST/JSON API to admins, end +users, and integrations; runs system VMs (Secondary Storage VM, Console +Proxy VM, virtual router); and orchestrates a fleet of **agents** running +on each hypervisor host. Authorization is RBAC + multi-tenant +domain/account/project hierarchy. The deployment shape is "operator-run +private/public cloud control plane", not a hosted-as-a-service appliance. + +## §2 Scope and intended use + +### Intended use + +- A multi-tenant IaaS control plane deployed by an operator inside a + controlled datacenter or cloud, exposing compute / storage / network + orchestration to authenticated end users via APIs which return responses in JSON or XML and a Vue.js Web + UI, with separately authenticated administrators *(documented: `README.md`, + `INSTALL.md`)*. +- Used both for service-provider public clouds and for on-premises private + clouds; the trust model is the same in both *(documented: `README.md`)*. + +### Deployment shape + +CloudStack is **not** an in-process library, **not** a single-binary +appliance, and **not** a hosted SaaS. It is a distributed control plane: +one or more management-server instances — **a single management-server +instance for smaller clouds, or a cluster behind a load balancer for +larger deployments** *(maintainer: DaanHoogland)* — a MariaDB/MySQL +database, one usage server, an optional +SecondaryStorageVM/ConsoleProxyVM/VirtualRouter set of system VMs, and a +per-hypervisor-host `cloudstack-agent` (for KVM/baremetal) or +out-of-process resource bridges (for VMware / XenServer / XCP-ng / Hyper-V). +The operator owns the surrounding L2/L3 network (the **management network**, +the **public network**, the **guest network**, the **storage network**) +and the physical hosts. The threat model is therefore that of a +distributed service (single-instance or clustered), not a library +*(maintainer: DaanHoogland — confirms the distributed control-plane shape; +single-instance is also a supported topology)*. + +### Caller roles + +| Role | Trust level | Notes | +| --- | --- | --- | +| **End-user API client / Web UI user** | untrusted but authenticated | Identity verified via Apache CloudStack-native (password + HMAC-SHA1 signed request), LDAP, SAML2, OAuth2, or pluggable `APIAuthenticator` *(documented: `plugins/user-authenticators/{ldap,saml2,oauth2,...}`, `server/src/main/java/com/cloud/api/ApiServer.java` `verifyRequest`)*. | +| **Domain / Project admin** | partial trust within their domain | Bounded by RBAC (`plugins/acl/{static,dynamic,project}-role-based`) and the domain hierarchy; can manage users / VMs / networks within a domain. | +| **Root admin** | trusted control plane | Global RBAC role; can change global configuration, upload templates/ISOs, run privileged orchestration. | +| **Operator / cluster admin** | trusted | OS-level access to management-server hosts, the MariaDB database, the keystore, and the agent hosts. Sets `agent.properties`, manages `cloudstack-agent` packages, manages the JCEKS keystore used by the agent for TLS *(documented: `agent/conf/agent.properties`, `framework/security/.../KeystoreManager.java`)*. | +| **Hypervisor agent (cloudstack-agent on KVM/baremetal)** | trusted-once-enrolled peer | Mutually authenticated via X.509 client cert signed by the management server's Root CA *(documented: `framework/ca/`, `plugins/ca/root-ca/`, `agent/src/main/java/com/cloud/agent/Agent.java` `setupAgentKeystore`)*. | +| **System VM (SSVM / CPVM / VR)** | trusted-once-enrolled peer | Same X.509 enrolment shape as the agent; carries the agent binary inside *(maintainer: confirmed — same trust tier as agents, not a separate tier)*. | +| **Hypervisor host (the underlying KVM/VMware/etc.)** | trusted by virtue of operator-controlled provisioning | CloudStack expects to drive the hypervisor via libvirt / VMware vSphere SDK / XenAPI as a privileged user *(documented: `plugins/hypervisors/kvm/`, `plugins/hypervisors/vmware/`, `plugins/hypervisors/xenserver/`)*. | +| **Hypervisor-managed guest VM (end-user workload)** | **untrusted** | A guest VM is an attacker's workload; the model defends against it. | +| **Reverse proxy / load balancer in front of management server** | trusted *(if `proxy.header.verify=true`)* | When the operator enables forward-header processing, only requests whose `Remote_Addr` ∈ `proxy.cidr` have their `proxy.header.names` header honoured *(documented: `server/src/main/java/com/cloud/api/ApiServlet.java` `getClientAddress`; setting names maintainer: vishesh92)*. | +| **Underlying storage (primary / secondary)** | trusted by virtue of operator-granted credentials | CloudStack reads/writes via NFS / RBD / iSCSI / S3 with operator-supplied credentials *(documented: primary/secondary storage plugins under `plugins/storage/`)*. | +| **External integrations (Tungsten, NSX, Netscaler, Palo Alto, …)** | trusted control-plane peers | Operator-configured; CloudStack assumes truthful responses *(inferred — §14 Q3)*. | + +### Component-family table + +| Management server JSON and XML APIs | `server/src/main/java/com/cloud/api/ApiServlet.java`, HTTP on `:8080` (API + UI), optional HTTPS on `:8443` when `https.enable=true`; user API path `:8080/client/api` *(documented: `server/src/main/java/com/cloud/api/ApiServlet.java`, `client/conf/server.properties.in`)* | network (TCP, optionally TLS) | **yes** | +| Management server cluster RPC (peer-to-peer) | NIO + TLS between management-server replicas, `:9090` *(documented: `framework/cluster/`, `utils/.../nio/`)* | network | **yes** (peer auth via Root CA) | +| Management server → agent RPC | NIO + TLS on `:8250` (default `agent.properties`) *(documented: `agent/conf/agent.properties` line 47, `utils/.../nio/NioServer.java`)* | network | **yes** (mutually authenticated via Root CA) | +| `cloudstack-agent` (KVM/baremetal) | reverse-connects to management server, runs commands via libvirt / hypervisor SDK *(documented: `agent/`, `plugins/hypervisors/kvm/`)* | network + hypervisor + OS | **yes** | +| System VMs — SecondaryStorageVM, ConsoleProxyVM, Virtual Router | shipped images under `systemvm/`; agent binaries inside them *(documented: `systemvm/`)* | network (storage / public / guest) | **yes** | +| Console proxy data path | browser ↔ ConsoleProxyVM ↔ hypervisor VNC/SPICE socket; signed token issued by management server *(documented: `server/src/main/java/com/cloud/servlet/ConsoleProxyServlet.java`, `server/src/main/java/com/cloud/servlet/ConsoleProxyPasswordBasedEncryptor.java`)* | network | **yes** | +| Secondary-storage HTTP (templates, ISO downloads, snapshot copies) | download links are UUID-named symlinks served by an Apache httpd, with **no auth on the link**; the UUID format prevents enumeration and the symlinks are removed after a period *(maintainer: vishesh92, DaanHoogland)* | network | **yes** | +| Hypervisor plugins (`plugins/hypervisors/{kvm,vmware,xenserver,hyperv,ovm,ovm3,baremetal,ucs,simulator}`) | invoked by agent or by management server *(documented: `plugins/hypervisors/`)* | hypervisor APIs | **yes** for the call shape; **out-of-model** for the upstream hypervisor's own bugs | +| Network plugins (`plugins/network-elements/{netscaler,nsx,palo-alto,tungsten,nicira-nvp,...}`) | management server outbound | external SDN/firewall APIs | **yes** for credential handling and request construction; **out-of-model** for the external endpoint | +| Storage plugins (`plugins/storage/{volume,image,object}`) | management server / agent | NFS, RBD, iSCSI, S3 endpoints | **yes** for credential handling; **out-of-model** for the storage endpoint | +| User authenticator plugins (`plugins/user-authenticators/{md5,sha256salted,pbkdf2,plain-text,ldap,saml2,oauth2}`) | management server | LDAP / SAML2 IdP / OAuth2 IdP | **yes** for the local code; **out-of-model** for the IdP | +| RootCA provider (`plugins/ca/root-ca/`) | self-signed CA generated by management server at first boot, issues certs to agents *(documented: `plugins/ca/root-ca/.../RootCAProvider.java`)* | none directly | **yes** | +| Two-factor authenticators (`plugins/user-two-factor-authenticators/{static-pin,totp}`) | management server | none | **yes** | +| Backup providers (`plugins/backup/`) | management server outbound | external backup endpoints | **yes** for credential handling | +| Quota / metrics / DRS / HA planners | internal | none | **yes** as orchestration only; not a security boundary | +| Database layer (MariaDB/MySQL, Jasypt-encrypted secrets) | management server | network to DB | **yes** for credential handling; DB itself is trusted *(documented: `README.md` "Notice of Cryptographic Software" — JaSypt, native DB encryption)* | +| `cloud-cli`, `tools/marvin`, `test/`, `developer/`, `quickcloud/` | integration / test tooling | varies | **out of model** *(§3)* | +| `systemvm/agent/noVNC` (a vendored fork of `github.com/novnc/novnc` with CloudStack-specific changes on top *(maintainer: vishesh92)*), `…/vendor/pako`, other vendored JS / shell scripts | vendored upstream | n/a | in-model only at the wrapper boundary; upstream bugs go upstream. No automated vendored-dependency update procedure exists today (dependabot does not produce viable PRs); the PMC would prefer to have one *(maintainer: DaanHoogland)* | + +## §3 Out of scope (explicit non-goals) + +CloudStack is not, and does not aim to be, the following — reports +requiring any of these will be closed with the cited disposition: + +1. **A defender against the operator.** Anyone with `root` on a + management-server host, `root` on a hypervisor host, raw MariaDB + credentials, the JCEKS keystore + `security.encryption.key` / + `security.encryption.iv` *(documented: + `framework/security/.../KeysManager.java`)*, or the Root CA private key + already has unbounded power. "The operator misconfigured X" is not a + vulnerability *(inferred — §14 Q6)*. → `OUT-OF-MODEL: + adversary-not-in-scope`. +2. **A defender against a malicious external service the operator + configured.** A hostile LDAP server, SAML IdP, OAuth IdP, Tungsten / + NSX / Netscaler controller, S3 endpoint, Ceph cluster, or backup + provider is treated as a trusted control-plane peer. If the report + requires that peer to be hostile, it is out of model *(inferred — + §14 Q3)*. → `OUT-OF-MODEL: trusted-input`. +3. **A defender against the hypervisor.** CloudStack drives KVM / VMware / + XenServer / XCP-ng / Hyper-V via their own admin APIs. A hypervisor + bug that allows guest escape, a vSphere SDK vulnerability, a libvirt + privilege escalation — all are upstream to the hypervisor project, not + to CloudStack *(inferred — §14 Q7)*. → `OUT-OF-MODEL: + unsupported-component` (upstream pointer). +4. **An isolation boundary between an authorized administrator's API + call and the management server process.** Root admin can change global + configuration, upload templates and scripts to system VMs, register + arbitrary network/storage plugins, and run `runCustomAction`-style + commands. A new way for a root admin to do something they are already + authorized to do is not a vulnerability *(maintainer: vishesh92 — §14 Q8)*. → + `OUT-OF-MODEL: equivalent-harm`. +5. **A defender against a guest VM doing things the hypervisor allows it + to do.** A guest VM consuming CPU, memory, or disk up to its allocated + limit, sending arbitrary IP traffic within its assigned VLAN / VXLAN / + security group, or exploiting another VM via the hypervisor's own + shared surfaces (sidechannel, RowHammer, GPU passthrough leak) is out + of model. CloudStack is responsible only for the orchestration that + *places* the guest, not for hypervisor-level isolation *(maintainer: + vishesh92 — §14 Q9; the in-model case is CloudStack applying + wrong/insecure hypervisor settings — Daan to confirm boundary)*. → `OUT-OF-MODEL: adversary-not-in-scope` for the + side-channel case, `BY-DESIGN: property-disclaimed` for the + resource-limit case. +6. **A sandbox for templates, ISO images, or user-data scripts.** A + user-uploaded template (via `registerTemplate`) is run by the + hypervisor with the privileges the system grants. cloud-init / + user-data / metadata is passed through to the guest; CloudStack does + not parse or sanitize its semantics *(documented: kubernetes-service + plugin `userdata` references; maintainer: vishesh92 — §14 Q10, end-user guest customization)*. → + `BY-DESIGN: property-disclaimed`. +7. **Code that ships but is not part of the supported product:** + `tools/marvin/`, `test/`, `developer/`, `quickcloud/`, `cloud-cli/`, + `tools/devcloud4/`, `tools/devcloud-kvm/`, `tools/appliance/`, + `tools/checkstyle/`, `tools/transifex/`, `services/`-side simulators, + `simulator` hypervisor plugin, and IDE / build helpers under `tools/`. + *(inferred — §14 Q11)*. → `OUT-OF-MODEL: unsupported-component`. +8. **Bundled / vendored upstream libraries** — JaSypt, Bouncy Castle, + JSch, OpenSwan, noVNC + `pako`, MariaDB Connector/J, Spring, + Apache Commons, log4j, etc. *(documented: `README.md` Cryptographic + Software notice)*. `systemvm/agent/noVNC` is specifically a **vendored + fork of `github.com/novnc/novnc`** carrying CloudStack-specific changes + *(maintainer: vishesh92)*. Where CloudStack vendors source, the vendored + code is modeled at the wrapper boundary; vulnerabilities intrinsic to the + upstream project should be reported upstream. There is currently **no + automated procedure** to pull upstream fixes into the vendored copies + (dependabot has not produced viable PRs); the PMC would prefer to + establish one *(maintainer: DaanHoogland)*. + → `OUT-OF-MODEL: unsupported-component` (with an upstream pointer). +9. **The four satellite repos** (`apache/cloudstack-cloudmonkey`, + `apache/cloudstack-go`, `apache/cloudstack-terraform-provider`, + `apache/cloudstack-kubernetes-provider`) — covered by their own delta + threat models which inherit §3 / §4 / §7 from this document. +10. **The CloudStack documentation site, Confluence wiki, downloads + mirrors, Docker Hub images outside `apache/cloudstack-*`, gem / + npm / PyPI packages with similar names, and other non-product + surfaces.** Out of scope. + +## §4 Trust boundaries and data flow + +CloudStack has at least nine distinct trust transitions; a finding is +in-model only when it cleanly maps to one of them. + +| # | Transition | Authentication | Authorization | +| --- | --- | --- | --- | +| B1 | API client → management server JSON API (`:8080`/`:8443`) | per-user API key + HMAC-SHA1 signature over query string, or session login + 2FA *(documented: `server/src/main/java/com/cloud/api/ApiServer.java` `verifyRequest`)*; signature version 3 has expiration enforcement *(documented: same file line ~1053)* | RBAC (dynamic-role-based / static-role-based / project-role-based) on the called API command name + domain/account ownership of named resources | +| B2 | Web UI → management server (`:8080`) | same as B1 plus session cookie | same as B1 | +| B3 | Browser → ConsoleProxyVM → hypervisor VNC socket | signed token issued by management server, embedded in URL; encrypted with `ConsoleProxyPasswordBasedEncryptor` *(documented: `server/src/main/java/com/cloud/servlet/ConsoleProxyServlet.java`, `ConsoleProxyPasswordBasedEncryptor.java`)* | implicit (signed-token possession) | +| B4 | Management server ↔ management server (cluster peers) | NIO + TLS, Root CA-issued certs *(documented: `framework/cluster/`, `framework/ca/`)* | peer-trust by valid cert | +| B5 | Management server → `cloudstack-agent` (KVM/baremetal) | NIO + TLS on `:8250`; agent uses X.509 client cert issued by Root CA on first connect; cert provisioning is the `SetupKeyStoreCommand` shape *(documented: `agent/src/main/java/com/cloud/agent/Agent.java` `setupAgentKeystore`, `framework/ca/.../CAService.java`, `plugins/ca/root-ca/.../RootCAProvider.java`)*; trust strictness governed by `ca.plugin.root.auth.strictness` (**default `true` for new setups; `false` only on upgrade from pre-Aug-2017 versions** — see §5a) and `ca.plugin.root.allow.expired.cert` | peer-trust by valid cert | +| B6 | Management server → external services (LDAP / SAML2 / OAuth2 IdP, NSX, Netscaler, Tungsten, S3, backup providers) | per-provider (service account, OAuth token, etc.) | external-service-side | +| B7 | Agent → hypervisor (libvirt / vSphere SDK / XenAPI) | local Unix socket (libvirt) or operator-supplied SDK credentials | hypervisor-side | +| B8 | Management server / agent → primary/secondary storage (NFS, RBD, iSCSI, S3) | OS-level (NFS), Ceph cephx, iSCSI CHAP, IAM key / static credential (S3) | storage-side | +| B9 | Operator → management server config (`db.properties`, `server.properties`, JCEKS keystore, global config table) | filesystem permissions on the host + DB access | OS-level + DB-level | + +### Reachability preconditions per family + +For each family in §2, a finding is in-model only if it is reachable as +follows: + +- **Management server JSON API**: reachable from an *unauthenticated* network + peer who can reach `:8080` / `:8443`. Findings that require an + authenticated peer collapse to "authenticated user with RBAC privilege + X", and must additionally either clear RBAC for the harmful command or + bypass it. +- **Web UI**: same shape as the JSON API; the Vue.js SPA is a presentation + layer over the API. +- **Cluster RPC (B4)**: reachable from a peer that has cleared the Root CA + trust check. A flat "cluster RPC has no auth" finding is `OUT-OF-MODEL: + adversary-not-in-scope` because the model *requires* the Root CA to be + enrolled across peers; a *cleartext*/un-certed cluster RPC finding is + gated by `ca.plugin.root.auth.strictness`, which defaults to `true` on + new setups (see §5a). +- **Management ↔ agent (B5)**: reachable from a peer that presents a + Root-CA-signed certificate the management server accepts. By default on + new setups `ca.plugin.root.auth.strictness = true`, so the management + server **does require** a client certificate from the connecting agent + *(maintainer: vishesh92 — + `https://github.com/apache/cloudstack/pull/2239`)*. The value remains + `false` only when upgrading from versions released before Aug 2017 that + predate the setting; that upgrade case is documented in the upgrade + instructions and is therefore not a concern *(maintainer: DaanHoogland)* + *(documented: `plugins/ca/root-ca/.../RootCAProvider.java`, + `RootCACustomTrustManager.java`)*. +- **Console proxy (B3)**: reachable by anyone who holds a valid signed + token. The token is the entire authorization gate. +- **Agent → hypervisor (B7)**: reachable only on the agent host, by code + the agent runs. +- **External integrations (B6)**: reachable from the management server's + outbound posture; a hostile external service is `OUT-OF-MODEL: + trusted-input` (§3 item 2). + +## §5 Assumptions about the environment + +- **Operating system (management server / usage server)**: RHEL 8/9/10, + CentOS 8/9, Rocky 9/10, Ubuntu 22.04/24.04, SUSE 15, openSUSE Leap 15; + Java 17 (`README.md`, `INSTALL.md`, `packaging/{el8,el9,el10,debian,suse15}`). +- **Operating system (agent)**: same family on KVM/baremetal hosts; + agent ships as `cloudstack-agent` package *(documented: `debian/`, + `packaging/`)*. +- **Database**: MariaDB or MySQL-compatible, accessible from each + management-server instance; CloudStack uses native DB encryption + + JaSypt for application-level secrets *(documented: `README.md` + "Notice of Cryptographic Software")*. +- **Cryptography**: JaSypt (application-secret encryption), Bouncy Castle + (general-purpose crypto, X.509 issuance in the Root CA provider), JSch + (SSH client to system VMs), OpenSwan (optional VPN endpoint termination) + *(documented: `README.md` Cryptographic Software notice)*. +- **Network**: operator-controlled L2/L3 with at least the management + network, public network, guest network, and storage network as logical + fabrics *(documented: CloudStack admin documentation; inferred — + §14 Q13)*. The management network is the trusted control-plane + network; the guest network carries untrusted guest VM traffic. +- **Time**: signature version 3 enforces an `expires` parameter on signed + API requests *(documented: `ApiServer.java` line ~1054)*; this assumes + loosely-synchronized clocks between client and management server + *(inferred — §14 Q14)*. +- **Filesystem**: the JCEKS keystore, `db.properties`, `server.properties`, + and Root CA private key are stored under `/etc/cloudstack/management/` + with OS-level permissions restricted to the `cloudstack` user + *(inferred — §14 Q15)*. +- **Hypervisor**: each supported hypervisor is assumed to provide its own + guest isolation (memory, vCPU, disk, network) and to expose a stable + admin API (libvirt for KVM, vSphere SDK for VMware, XenAPI for + XenServer/XCP-ng, WinRM/Hyper-V API for Hyper-V). +- **What CloudStack does to its host** (negative claims, awaiting + maintainer ratification): + - **does** open listening sockets on documented ports + (`:8080`/`:8443`/`:8250`/`:8096`/`:9090`/console-proxy ports) *(documented)*; + - **does** maintain MariaDB connections from the management server; + - **does** issue X.509 certificates from its self-signed Root CA *(documented: + `plugins/ca/root-ca/.../RootCAProvider.java`)*; + - **does** spawn child processes from the agent (`Script` invocations + against `/usr/share/cloudstack-common/scripts/`) *(documented: + `agent/src/main/java/com/cloud/agent/Agent.java` `keystoreSetupSetupPath`, + `keystoreCertImportScriptPath`)*; + - **does** write logs under operator-configured locations; + - **does** read a documented set of environment variables and the + `db.properties` file at startup *(inferred — §14 Q16)*; + - **does** install signal handlers / shutdown hooks only as + Tomcat/Jetty servlet container default *(inferred — §14 Q16)*. + +## §5a Build-time and configuration variants + +CloudStack ships as a family of packages: 'cloudstack-agent', 'cloudstack-baremetal-agent', 'cloudstack-common', 'cloudstack-integration-tests', 'cloudstack-management', 'cloudstack-marvin', 'cloudstack-mysql-ha', 'cloudstack-ui', 'cloudstack-usage' +*(documented: `debian/`, `packaging/`)*. A sizable number of runtime +configuration knobs materially change the security envelope. The +security-relevant subset: + +| Knob | Default | Maintainer stance | Effect | +| --- | --- | --- | --- | +| `ca.plugin.root.auth.strictness` | **`true` for new setups; `false` only on upgrade from pre-Aug-2017 versions** *(maintainer: vishesh92 — `https://github.com/apache/cloudstack/pull/2239`)* | New setups are strict by default; the `false`-on-upgrade case is called out in the upgrade instructions and is therefore not a concern *(maintainer: DaanHoogland)* | When `false`, the management server's `RootCACustomTrustManager` does **not** require a client certificate from a peer attempting to connect on `:8250` (agent port) or cluster ports. A peer without a cert is allowed in. | +| `ca.plugin.root.allow.expired.cert` | **`true`** *(documented: `RootCAProvider.java`)* | operational default to survive cert-rotation lag *(maintainer: paired with the strictness ruling above)* | When `true`, an expired client cert is accepted during SSL handshake. | +| `ca.plugin.root.issuer.dn` | `CN=ca.cloudstack.apache.org` *(documented: same file)* | configured at first management-server boot | Subject DN of the auto-generated self-signed Root CA. | +| `proxy.header.verify` | `false` by default *(maintainer: vishesh92 — §14 Q17)* | When on, the operator must restrict `proxy.cidr` to the trusted reverse-proxy CIDR | When set, `ApiServlet.getClientAddress` honours proxy-set forward headers *only* for source IPs in `proxy.cidr` *(documented: `server/src/main/java/com/cloud/api/ApiServlet.java` `getClientAddress`; setting name maintainer: vishesh92)*. | +| `proxy.header.names` | list of header names; semantics: names to check for allowed IP addresses from a proxy-set header *(maintainer: vishesh92)* | list of header names to consult for the allowed client address when set by a proxy | Names the request header(s) carrying the proxy-set client IP. | +| `proxy.cidr` | unset *(maintainer: vishesh92 — §14 Q17; headers honoured only when `Remote_Addr` ∈ this list)* | required when `proxy.header.verify` is on | List of CIDRs for which `proxy.header.names` headers are honoured when the connecting `Remote_Addr` is in this list *(semantics maintainer: vishesh92)*. | +| `enable.user.2fa` / `mandate.user.2fa` | both default `false`; domain-configurable *(maintainer: vishesh92 — §14 Q18)* | `enable.user.2fa` turns 2FA on; `mandate.user.2fa` makes it mandatory (only when `enable.user.2fa` is true) — a deployment choice, not a §10 violation when off | When on, users must complete static-pin or TOTP 2FA after login. | +| `security.encryption.key`, `security.encryption.iv` | auto-generated at first boot *(documented: `framework/security/.../KeysManager.java`)* | trusted secret | Base64-encoded JaSypt master key + IV used to encrypt application-level secrets in the DB. | +| `user.password.encoders.order` | `PBKDF2,SHA256SALT,MD5,LDAP,SAML2,PLAINTEXT` *(maintainer: vishesh92)* | first encoder in the order is used to hash new passwords; the list also defines the verification fall-through order | Governs how user passwords are stored and which encoders are accepted on verify. | +| `user.password.encoders.exclude` | `MD5,LDAP,PLAINTEXT` *(maintainer: vishesh92)* | excluded encoders are not used to (re)hash passwords | Excludes weak/legacy encoders from being chosen, even though they remain in the order list for verifying already-stored hashes. | +| `enforce.post.requests.and.timestamps` | per `isPostRequestsAndTimestampsEnforced` *(documented: `ApiServer.java`; setting name maintainer: vishesh92)* | bounds `expires` to a maximum future offset | Prevents an attacker who steals a signed URL with a 10-year expiration from using it forever. | +| `integration.api.port` (`:8096`) | typically disabled *(inferred — §14 Q20)* | When non-zero, exposes an *unauthenticated* admin API for integration testing | An open integration port is a complete RBAC bypass on the management server. | +| Hypervisor enablement (which `plugins/hypervisors/*` are installed and configured) | per zone | operator-driven | An unused hypervisor plugin still ships but is not connected to any host. | +| Hostname / SAN of management-server cert (`ca.framework.cert.management.custom.san`) | unset *(maintainer: vishesh92)* | when set, included in the auto-generated cert SAN | governs which hostnames clients can use to reach the management server. | +| SAML2 / OAuth2 enablement (`plugins/user-authenticators/{saml2,oauth2}`) | off *(inferred — §14 Q19)* | turning on adds an external IdP trust dependency | adds B6 transitions. | +| LDAP enablement (`plugins/user-authenticators/ldap`) | off *(inferred — §14 Q19)* | turning on adds an external LDAP trust dependency | adds B6 transitions. | + +**The Root-CA strictness default (resolved).** Earlier drafts treated +`ca.plugin.root.auth.strictness = false` as the shipped default and the +single highest-leverage open question. The PMC has clarified that **new +setups default to `true`** — the management server *does* require a +Root-CA-signed client cert on `:8250` and the cluster ports — and the +value is `false` **only** when upgrading from versions released before +Aug 2017 that predate the setting *(maintainer: vishesh92 — +`https://github.com/apache/cloudstack/pull/2239`)*. That upgrade case is +documented in the upgrade instructions, so a leftover `false` after such +an upgrade is an operator-hardening/upgrade-hygiene item, not a shipped +insecure default *(maintainer: DaanHoogland)*. A report against an open +`:8250` accepting an un-certed peer on a **new** install is therefore +`MODEL-GAP`/`VALID` (strictness should be on), whereas the same on an +**upgraded** pre-2017 install is `OUT-OF-MODEL: non-default-build` +(documented upgrade step not applied). `ca.plugin.root.allow.expired.cert` +remains `true` as an operational concession to cert-rotation lag. + +## §6 Assumptions about inputs + +### Per-endpoint trust table (network surfaces) + +| Surface / route | Parameter | Attacker-controllable? | Caller must enforce | +| --- | --- | --- | --- | +| Management server `:8080`/`:8443` JSON API | command name + params | **yes** | nothing — CloudStack parses, authenticates (B1), applies RBAC, dispatches | +| Management server `:8080`/`:8443` JSON API | `signature` parameter | **yes** | HMAC-SHA1 verified *constant-time* against expected signature *(documented: `ApiServer.java` line 1137 `ConstantTimeComparator.compareStrings`)* | +| Management server `:8080`/`:8443` JSON API | `expires` parameter (sig v3) | **yes** | rejected if past, or beyond the `enforce.post.requests.and.timestamps` ceiling *(documented: same file; setting name maintainer: vishesh92)* | +| Management server `:8080`/`:8443` JSON API | proxy-set forward headers (`proxy.header.names`) | **yes** if `proxy.header.verify=true` | honoured **only** if the connecting `Remote_Addr` ∈ `proxy.cidr` *(documented: `ApiServlet.java` `getClientAddress`; setting names maintainer: vishesh92)* | +| Management server `:8080`/`:8443` Web UI | session cookie | **yes** | session-fixation / invalidation handled via `invalidateHttpSession` on auth failure *(documented: `ApiServlet.java` line 418)* | +| Integration API `:8096` (if enabled) | command name + params | **yes** | **no signature check** — integration port is unauthenticated by design | +| Management ↔ agent `:8250` | NIO Thrift-like payload | **only by a peer that has cleared B5 trust** | client cert via `RootCACustomTrustManager` | +| Management ↔ cluster peer | NIO payload | **only by a peer that has cleared B4 trust** | client cert via `RootCACustomTrustManager` | +| Console proxy URL | encrypted token (containing VM identity + endpoint + duration) | **yes** | token MUST decrypt + verify with `ConsoleProxyPasswordBasedEncryptor` keys *(documented: `ConsoleProxyPasswordBasedEncryptor.java`)* | +| Secondary-storage HTTP download URL | UUID-named symlink path | **yes** | **no auth on the download link**; the UUID format is the anti-enumeration control and the symlink is removed after a period — timed availability of the download token is the mitigation *(maintainer: vishesh92, DaanHoogland)* | +| Template / ISO upload | URL of remote source | **yes** within RBAC | upload-gated by `registerTemplate` RBAC; bytes are then served to hypervisors as image data | +| User-data / metadata service (`169.254.169.254` from inside guests) | guest-controlled bytes (the request) | **yes from the guest**, but the service is reached *from the guest* and serves only that guest's data | guest-VM-side isolation by virtual router | +| Hypervisor agent log / event stream | bytes from hypervisor | trusted operator surface | none — assumed truthful | +| LDAP / SAML / OAuth response (B6) | bytes from IdP | trusted | LDAP queries treat returned attributes as authoritative | +| Storage response (B8) | bytes / metadata from storage | trusted | bytes are object content; envelope is control-plane | + +### Size / shape / rate + +- CloudStack does not document a maximum signed-API request size; assumed + to be servlet-container default (Jetty / Tomcat) *(inferred — §14 Q21)*. +- API rate limiting is per-account via the global config knobs `api.throttling.*` + *(inferred — §14 Q22)*; an attacker with a valid API key can be rate- + limited at the application layer. +- Template / ISO upload size is bounded by storage capacity and per-account + resource limits *(inferred — §14 Q22)*; pathological compressed-image + inputs (e.g. extremely compressible QCOW2 with sparse holes that expand + to TB on extraction) are robustness concerns *(inferred — §14 Q23)*. +- Cluster-peer and agent RPC payload sizes: no documented application-layer + cap; NIO framing applies *(inferred — §14 Q21)*. + +## §7 Adversary model + +### Actors + +| Actor | In scope? | Capabilities granted | +| --- | --- | --- | +| Unauthenticated network peer reaching `:8080`/`:8443` | **yes** | TCP to the listening ports; may attempt authentication; may attempt to violate the protocol pre-auth | +| Unauthenticated peer reaching `:8250` (agent port) | **only if** `ca.plugin.root.auth.strictness = false`, which on new setups it is **not** (default `true`); `false` arises only on un-remediated pre-Aug-2017 upgrades (§5a) | TCP to the listening port; may attempt to connect as a peer without presenting a cert | +| Unauthenticated peer reaching `:8096` (integration port) | **yes** *if* the port is open (typically not in production) | full unauthenticated admin API | +| Authenticated end user with limited RBAC role | **yes** | call APIs their role permits; manage VMs/networks/storage in their domain/account/project | +| Authenticated end user with broad RBAC role | partial | only RBAC-envelope escapes are in scope | +| Authenticated domain admin | **yes** | full management within their domain; cross-domain leakage is in scope | +| Authenticated root admin | **out of scope** — see §3 item 4 | unbounded by design | +| Co-tenant (different account in same domain or different domain on same CloudStack) | **yes** | cross-tenant leakage (VM ID guessing, network bleed, storage bleed, template visibility) is in scope | +| Guest VM workload | **partial** | hypervisor-mediated; out-of-scope for hypervisor isolation bugs (§3 item 5), in-scope for the orchestration that placed the VM (security-group rule application, VLAN tagging, public IP routing) | +| Browser holding a valid console-proxy URL | **yes** | the URL is a bearer credential; scope of harm is one VM's console for the URL's lifetime | +| Operator | **out of scope** | see §3 item 1 | +| Hostile hypervisor | **out of scope** | see §3 item 3 | +| Hostile LDAP / SAML / OAuth IdP, hostile NSX/Netscaler/Tungsten, hostile S3 endpoint | **out of scope** | see §3 item 2 | +| Reverse proxy that should be trusted but is not in `proxy.cidr` | **out of scope** | its forward headers are not honoured | +| Local process on the management-server host running as a different UID | **partial** *(inferred — §14 Q24)* | same-host attackers with non-cloudstack UID can reach `:8080` unless host firewalling forbids; CloudStack does not defend against same-host `root` | +| Side-channel observer (cache timing, network timing, hypervisor side channels) | **out of scope** *(inferred — §14 Q25)* | n/a | +| Quantum adversary | **out of scope** | n/a | + +### Authenticated-but-Byzantine peer (distributed-systems threshold) + +CloudStack is **not** a Byzantine-fault-tolerant system. A compromised +management-server cluster peer with a valid Root-CA-issued cert can +schedule arbitrary work onto the agent fleet, read any guest's data, and +hand out console-proxy tokens. The cluster trusts its own membership +*(inferred — §14 Q26)*. Likewise, a compromised agent host can serve +malicious data on the management network and produce wrong status. → +reports requiring a Byzantine internal peer are `OUT-OF-MODEL: +adversary-not-in-scope`. + +## §8 Security properties the project provides + +For each property: condition, violation symptom, severity tier, provenance. + +### P1 — Authentication of API clients via signed request + +- **Condition**: a request carries `apiKey` + `signature` (and, for + signature version 3, an `expires` parameter not in the past) + *(documented: `ApiServer.java` `verifyRequest`)*; the signature is + HMAC-SHA1 of the canonical parameter string under the per-user + secret key, base64-encoded, lowercased, URL-decoded, and compared + to the computed value using `ConstantTimeComparator.compareStrings` + *(documented: same file line 1137)*. +- **Violation symptom**: a request executes API commands without a + valid `apiKey`+`signature` pair (and without a valid session + cookie / SAML / OAuth / LDAP login). +- **Severity**: **security-critical**, `VALID` per §13. +- *(documented)* + +### P2 — Session authentication via password + optional 2FA + +- **Condition**: user logs in via the `login` API; 2FA is verified after + password if enabled for the user / domain *(documented: `ApiServlet.java` + lines 360–582)*. +- **Violation symptom**: a session is created without a valid password, or + 2FA enforcement is bypassed for a user where it is mandated. +- **Severity**: **security-critical**, `VALID` per §13. +- *(documented)* + +### P3 — Constant-time signature comparison + +- **Condition**: applies to the API signature check. +- **Violation symptom**: timing-side-channel measurement of signature + comparison reveals the expected signature byte-by-byte. +- **Severity**: **security-critical**, `VALID` per §13. +- *(documented: `ApiServer.java` line 1137)* + +### P4 — Authorization via RBAC + domain/account/project hierarchy + +- **Condition**: the authenticated principal calls an API command, and the + command name is permitted for their role *(documented: `plugins/acl/{static,dynamic,project}-role-based`)*; + resources named in the request belong to the principal's domain/account/project + or to a child within the principal's scope. +- **Violation symptom**: a non-root principal successfully executes an + API command not licensed for their role, or operates on a resource + outside their domain/account/project scope. +- **Severity**: **security-critical**, `VALID` per §13. +- *(documented)* + +### P5 — Mutual TLS on management ↔ agent, management ↔ cluster peer, *when configured* + +- **Condition**: `ca.plugin.root.auth.strictness = true` — **the default + on new setups** *(maintainer: vishesh92 — + `https://github.com/apache/cloudstack/pull/2239`)*. Pre-Aug-2017 + upgrades may leave it `false` until the documented upgrade step is + applied *(maintainer: DaanHoogland)*. `ca.plugin.root.allow.expired.cert` + remains `true` (cert-rotation concession), so the property covers + *peer-cert presence and Root-CA chain*, not cert freshness. +- **Violation symptom**: a peer without a Root-CA-issued cert successfully + completes a session on `:8250` or the cluster port on a setup where + strictness is on. +- **Severity**: **security-critical**, `VALID` per §13. +- *(documented; default resolved by maintainer.)* + +### P6 — Reverse-proxy IP-trust gating for forward headers + +- **Condition**: `proxy.header.verify` on (default `false`) *(maintainer: + vishesh92 — §14 Q17)*; + only requests whose `Remote_Addr` falls in `proxy.cidr` have their + `proxy.header.names` forward header(s) consulted *(documented: + `ApiServlet.java` `getClientAddress` `NetUtils.isIpInCidrList`; setting + names maintainer: vishesh92)*. +- **Violation symptom**: a request from a source IP **outside** + `proxy.cidr` succeeds with an attacker-supplied forward header + taking effect. +- **Severity**: **security-critical**, `VALID` per §13. +- *(documented)* + +### P7 — Console-proxy token confidentiality and integrity + +- **Condition**: tokens are encrypted under the + `ConsoleProxyPasswordBasedEncryptor` keys *(documented: + `ConsoleProxyPasswordBasedEncryptor.java`)*; a token includes the VM + identity, the hypervisor endpoint, and a duration / expiry. +- **Violation symptom**: a third party with no console-access RBAC + privilege forges or decrypts a token to gain console access; or a token + remains valid past its declared expiry. +- **Severity**: **security-critical**, `VALID` per §13. +- *(documented)* + +### P8 — Application-secret encryption at rest in the DB via JaSypt + +- **Condition**: `security.encryption.key` + `security.encryption.iv` are + initialised at first boot and kept under filesystem ACLs + *(documented: `framework/security/.../KeysManager.java`, + `README.md` Cryptographic Software notice)*. +- **Violation symptom**: an attacker with read access to the DB but not + to the encryption key file recovers plaintext for secrets the model + claims are encrypted (typically: external service passwords, account + API secret keys when stored encrypted). +- **Severity**: **security-critical**, `VALID` per §13. +- *(documented)* + +### P9 — Memory safety on well-formed inputs across documented surfaces (JVM-bounded) + +- **Condition**: input matches the documented protocol on B1–B5; the JVM + is conformant; native code is invoked only via documented hypervisor + SDKs (libvirt / vSphere / XenAPI). CloudStack presumes **no limitation + on implementation language** — ocaml, python and bash run on hypervisors + and go is used on the management server (the set may grow); the + memory-safety claims here hold for the **JVM components**, to which the + JVM-conformance condition applies *(maintainer: DaanHoogland — §14 Q27)*. +- **Violation symptom**: heap corruption, OOM-via-input-size attack on a + surface where the input source is `:8080` / `:8443` / B5; JVM-side + crashes from a request a normally-RBAC'd user could send. +- **Severity**: **security-critical** when reachable from network input; + **`VALID-HARDENING`** when reachable only by a writer who already + controls the bytes (§3 item 5). +- *(maintainer: DaanHoogland — §14 Q27)* + +### P10 — Bounded RBAC scope of cross-domain visibility (`SHOW`-equivalent listing) + +- **Condition**: `list*` API commands filter responses to the principal's + domain/account/project scope per `plugins/acl/` policy. +- **Violation symptom**: a `list*` response leaks resource IDs / names / + metadata for resources outside the principal's RBAC scope. +- **Severity**: **security-critical** for resources whose existence is + itself confidential (typically: customer VM names, custom template + names); `VALID` per §13. +- *(inferred — §14 Q28)* + +## §9 Security properties the project does *not* provide + +State each plainly so a triager can route an inbound report to the matching +disclaimer. + +- **No defence against the operator.** Anyone with root on a + management-server host, the JCEKS keystore + `security.encryption.key`, + the Root CA private key, or the MariaDB credentials wins. See §3 item 1 + *(inferred — §14 Q6)*. +- **No defence against a malicious external service the operator + configured.** A hostile LDAP/SAML/OAuth IdP, NSX controller, Tungsten, + Netscaler, S3 endpoint, or backup provider is trusted. See §3 item 2. +- **No defence against the hypervisor.** Guest VM escape via libvirt, + vSphere, XenAPI, Hyper-V is upstream. See §3 item 3. +- **No isolation between a root admin's API call and the management-server + process.** Root admin can register arbitrary plugins, upload arbitrary + templates, run `runCustomAction`. See §3 item 4 *(inferred — §14 Q8)*. +- **No sandbox for guest VM workloads beyond what the hypervisor provides.** + Side-channel leaks between co-tenant VMs (cache, branch, memory bus, + shared GPU) are out of scope. See §3 item 5 *(inferred — §14 Q9)*. +- **No sandbox for user-data / templates / ISOs.** Templates run as their + own OS image with their own cloud-init; CloudStack does not parse or + reject user-data semantics. See §3 item 6 *(inferred — §14 Q10)*. +- **No defence against decompression / decoding bombs in uploaded + templates / ISOs.** A pathological QCOW2 / RAW image can consume + arbitrary CPU / disk on extraction; per-account resource limits are the + bound *(inferred — §14 Q23)*. +- **No defence against intra-cluster Byzantine failure.** A compromised + cluster peer with a valid Root-CA-issued cert can read any data the + cluster can read; see §7 *(inferred — §14 Q26)*. Likewise a compromised + agent host. +- **No data-at-rest encryption beyond JaSypt for selected DB columns + + whatever storage layers provide.** Guest volumes are encrypted only if + the primary-storage plugin supports it (Ceph RBD encryption, LUKS at + hypervisor layer) and the operator has configured it *(inferred — + §14 Q29)*. +- **No defence against side-channel observation** of API request timing, + agent RPC timing, or memory access patterns *(inferred — §14 Q25)*. +- **No application-layer constant-time comparison of anything other than + the API signature.** Login password comparison, session cookie + comparison, console-token comparison — not documented constant-time + *(inferred — §14 Q30)*. +- **No defender stance against an attacker on the same Linux host running + as a non-`cloudstack` UID** — CloudStack defends only across the + network surface; same-host attackers with shell access on the + management-server host already have many paths to win *(inferred — + §14 Q24)*. +- **No supported posture for the integration API port (`:8096`).** When + open, it is an unauthenticated admin surface; closing it is the + operator's job *(inferred — §14 Q20)*. + +### False-friend properties (call out separately) + +- **The Root CA is self-signed and auto-generated** — it is *not* a + publicly-trusted CA. Browsers and external clients require manual trust + bootstrap. The Root CA private key resides on the management server; a + compromised management server compromises the entire agent fleet's + trust. +- **`ca.plugin.root.auth.strictness = false` is not "TLS off" — it is + "client cert not required"** *(documented: `RootCAProvider.java`)*. TLS + on the wire is still there; what is missing is the peer-cert check. + Note the value is `true` on new setups *(maintainer: vishesh92)*; a + scanner that flags "client cert not requested" is only correct on an + un-remediated pre-Aug-2017 upgrade, and even then it identifies a + documented upgrade step, not a transport-encryption bug. +- **`ca.plugin.root.allow.expired.cert = true` is the operational default + to survive cert-rotation lag** but is not a security boundary. +- **The HMAC-SHA1 signature is request-integrity over the URL, not + request encryption.** Transport encryption is TLS; if the operator + serves the API over `http://`, the signature still validates but the + whole request (including the secret-derived signature) is visible to + the network. +- **The console-proxy URL is a bearer credential.** Anyone who sees the + URL (in logs, in a proxy, in a shoulder-surf) holds the console for + the URL's lifetime. +- **`list*` filtering is a per-call authorization view, not an + information-flow channel.** Existence of a resource that the principal + cannot see may leak through error messages, async-job status, event + logs, or by-ID lookup probing *(inferred — §14 Q28)*. +- **The integration API port is not a "trusted" port in the sense of + Kerberos `auth-int` — it is *no authentication at all***. The name + invites confusion. +- **JaSypt-encrypted DB columns are *(documented)* protected against a + DB-only read.** They are *not* protected against an attacker who + obtains both the DB and the encryption-key file. + +### Well-known attack classes the project does not defend against + +- **Cross-tenant VM-ID guessing / template-name enumeration**: §10 misuse, + not engine breakage. +- **Decompression / decoding bombs in uploaded templates and ISOs**. +- **Hypervisor side-channel attacks between co-tenant VMs**. +- **Confused-deputy between RBAC role and resource ownership** — e.g. a + domain admin's role permits a command, but the resource named is in a + child domain they should not touch *(inferred — §14 Q28)*. +- **Time-of-check-to-time-of-use** between RBAC check at API entry and + the actual orchestration on the agent fleet — policy revocations + mid-job are not retroactively enforced *(inferred — §14 Q31)*. + +## §10 Downstream responsibilities + +The operator deploying CloudStack in production **must**: + +1. Keep `ca.plugin.root.auth.strictness = true` (the default on new + setups). When **upgrading from a pre-Aug-2017 version**, follow the + documented upgrade step to turn strictness on — otherwise agent and + cluster-peer ports accept peers without a cert *(maintainer: vishesh92, + DaanHoogland — `https://github.com/apache/cloudstack/pull/2239`)*. + Consider tightening `ca.plugin.root.allow.expired.cert` (default `true`) + once cert rotation is reliable. +2. Restrict the management network at L2/L3 so that `:8250` (agent), + `:9090` (cluster), and the MariaDB port are reachable only from the + intended peers *(inferred — §14 Q13)*. +3. Restrict the integration API port `:8096` — either disable it entirely + or limit it to a localhost/management subnet *(inferred — §14 Q20)*. +4. Terminate TLS for the JSON API and Web UI on `:8443` (not `:8080`); if + `:8080` is exposed at all, only behind a TLS-terminating reverse + proxy *(inferred — §14 Q32)*. +5. When using a reverse proxy, set `proxy.header.verify = true`, + `proxy.header.names` to the forward header(s) the proxy sets, *and* + `proxy.cidr` to the proxy's CIDR — leaving `proxy.cidr` unset/empty + means the header is ignored (safe-default per P6), but a misconfigured + wide CIDR is a trust-bypass *(setting names maintainer: vishesh92)*. +6. Protect the `security.encryption.key` / `security.encryption.iv` + files, the JaSypt-encrypted DB, the Root CA private key, and the + `cloudstack-management` Unix user's home directory at OS level. +7. Keep the password-encoder configuration at safe defaults: + `user.password.encoders.order` defaults to + `PBKDF2,SHA256SALT,MD5,LDAP,SAML2,PLAINTEXT` (so PBKDF2 is used to hash + new passwords) and `user.password.encoders.exclude` defaults to + `MD5,LDAP,PLAINTEXT` (so the weak encoders are not chosen for hashing, + only retained for verifying already-stored hashes) *(maintainer: + vishesh92)*. Do not remove `MD5`/`PLAINTEXT` from the exclude list in + production — the supported greenfield encoder set is + `PBKDF2,SHA256SALT,SAML2` *(maintainer: vishesh92 — §14 Q19)*. +8. Enable 2FA (`totp` or `static-pin`) for administrators and ideally for + all users — 2FA on/off is a deployment choice via `enable.user.2fa` + and `mandate.user.2fa` (both default `false`) *(maintainer: vishesh92 — + §14 Q18)*. +9. Rotate per-user API secret keys on personnel change and on suspected + compromise. +10. Treat user-uploaded templates and ISOs as crossing a trust boundary — + scan / quarantine before allowing into the supported-template set. +11. Apply per-account resource limits (vCPU / RAM / volume size / image + size) to bound decompression-bomb and orchestration-DoS attacks. +12. Configure storage-layer encryption (Ceph RBD encryption, LUKS at KVM, + vSphere VM Encryption, etc.) if data-at-rest encryption is required. +13. Secure each `cloudstack-agent` host: `cloudstack` Unix user, agent + keystore under `/etc/cloudstack/agent/`, root account, libvirt / + vSphere admin credentials. +14. Restrict console-proxy URLs: do not log them, do not embed them in + public responses, set a short token lifetime. +15. Audit API call logs (via the event-bus plugin) for anomalous patterns. + +## §11 Known misuse patterns + +- **Leaving `:8250` open to the world with `ca.plugin.root.auth.strictness=false` + on an upgraded pre-Aug-2017 cluster.** New setups default to `true`; + the `false` value only survives an upgrade where the documented step + was skipped *(maintainer: vishesh92, DaanHoogland)*. In that state any + peer can connect as an agent — an upgrade-hygiene gap, dispositioned + `OUT-OF-MODEL: non-default-build` (documented upgrade step not applied). +- **Exposing `:8096` (integration API) publicly.** Anyone reaching the + port executes admin API commands without auth. +- **Exposing `:8080` (HTTP JSON API) publicly without a TLS-terminating + reverse proxy.** Signed-request integrity holds, but the API secret- + key-derived signature is visible to any wire observer; replay within + the `expires` window is trivial. +- **Setting `proxy.header.verify=true` with `proxy.cidr` wider than + the actual reverse-proxy CIDR.** An attacker outside the proxy can + spoof a `proxy.header.names` header and claim any IP address for audit + logs and authentication-IP checks *(setting names maintainer: vishesh92)*. +- **Removing `MD5`/`PLAINTEXT` from `user.password.encoders.exclude` (or + reordering them to the front of `user.password.encoders.order`) in + production.** The encoders ship for verifying legacy hashes; promoting + them to hash new passwords stores weakly-protected credentials + *(maintainer: vishesh92 — §14 Q19; the supported greenfield encoder set is `PBKDF2,SHA256SALT,SAML2`)*. +- **Granting domain admin to too many users.** A domain admin can manage + all accounts within the domain — including reading guest console URLs. +- **Embedding console-proxy URLs in screenshots, ticketing systems, or + chat.** Tokens are bearer credentials. +- **Reusing `security.encryption.key` across environments of different + trust levels.** A staging-env leak becomes a production-env decrypt + primitive *(inferred — §14 Q33)*. +- **Leaving `ca.plugin.root.auth.strictness=false` after a pre-Aug-2017 + upgrade in a multi-management-server deployment.** A peer can join the + cluster without a cert until the documented upgrade step flips it to the + new-setup default of `true` *(maintainer: vishesh92, DaanHoogland)*. +- **Uploading large or pathological templates and relying on hypervisor + to enforce size.** Per-account resource limits, not the engine, are the + enforcement. + +## §11a Known non-findings (recurring false positives) + +This section is the highest-leverage input for automated agentic security +scans. Each entry: tool symptom, why it is safe under the model, the § +that licenses the call. + +- **"Management ↔ agent port `:8250` accepts no client cert" reported + against a setup with `ca.plugin.root.auth.strictness=false`.** New setups + default to `true` and **do** require a Root-CA-signed client cert + *(maintainer: vishesh92 — `https://github.com/apache/cloudstack/pull/2239`)*. + The value is `false` only on an upgrade from a pre-Aug-2017 version that + predates the setting, and the upgrade instructions document turning it on + *(maintainer: DaanHoogland)*. → On a new install: `KNOWN-NON-FINDING` + (strictness is on). On an upgraded install with the step skipped: + `OUT-OF-MODEL: non-default-build` (documented upgrade step not applied). +- **"Integration port `:8096` is unauthenticated."** The port is + unauthenticated by design; operator responsibility per §10 to close / + bind to localhost. → `OUT-OF-MODEL: non-default-build` once the PMC + confirms. +- **"HMAC-SHA1 signature uses SHA1."** SHA1-HMAC is **not** broken for + HMAC use; collision attacks on SHA1 do not extend to HMAC-SHA1 + *(documented: cryptographic literature; CloudStack uses + `Mac.getInstance("HmacSHA1")` — `ApiServer.java` line 1130)*. → `KNOWN-NON-FINDING`. +- **"Constant-time string compare for the signature."** Already done — + `ConstantTimeComparator.compareStrings` per `ApiServer.java` line 1137. + → `KNOWN-NON-FINDING` (a finding flagging this is wrong). +- **"Root CA private key is on the management server."** By design — the + management server *is* the CA. → `BY-DESIGN: property-disclaimed`. +- **"Self-signed Root CA cert."** By design — the CA is generated at + first boot per `RootCAProvider.java`. Browsers will warn until the + operator bootstraps trust. → `BY-DESIGN: property-disclaimed`. +- **"Expired agent cert is accepted (`ca.plugin.root.allow.expired.cert=true`)."** + Documented default — an operational concession to cert-rotation lag, paired + with the strictness default *(maintainer: vishesh92, DaanHoogland)*. → + `VALID-HARDENING` at most; tightening it is an operator choice per §10. +- **"Hardcoded password / keytab in `tools/marvin/`, `test/`, `developer/`, + `quickcloud/`."** These directories are unsupported components per §3 + item 7. → `OUT-OF-MODEL: unsupported-component`. +- **"User-data / template contents execute arbitrary code in the guest + VM."** Templates are run as their own OS by the hypervisor; cloud-init + / user-data is intentionally a code-execution channel into the guest. + → `BY-DESIGN: property-disclaimed` per §9. +- **"Root admin can change global config / register plugins / upload + arbitrary templates."** Documented and intentional. → `BY-DESIGN: + property-disclaimed` per §9 / §3 item 4. +- **"DoS via expensive list call on a large CloudStack."** Pagination is + present; further bounds are admission-control / quota. → `BY-DESIGN: + property-disclaimed` per §9. +- **"Decompression bomb in an uploaded QCOW2 / template."** Per-account + resource limits are the bound. → `VALID-HARDENING` at most, unless the + decompression reaches §8 P9 memory-safety violations. +- **"Vendored Bouncy Castle / JaSypt / noVNC / `pako` has CVE-X."** Report + upstream; `systemvm/agent/noVNC` is a vendored fork of + `github.com/novnc/novnc` with CloudStack changes, and there is no + automated sync procedure today *(maintainer: vishesh92, DaanHoogland)*. → + `OUT-OF-MODEL: unsupported-component` (upstream pointer); a + CloudStack-introduced change *to* the fork is in-model. +- **"Secondary-storage download URL has no authentication / can be replayed."** + By design: download links are UUID-named symlinks served by an Apache + httpd with no auth on the link; the UUID format defeats enumeration and + the symlink is removed after a period, so timed availability is the + mitigation *(maintainer: vishesh92, DaanHoogland)*. → `BY-DESIGN: + property-disclaimed` for the no-auth aspect; a link that is *not* removed + after its window, or a guessable (non-UUID) name, is `VALID-HARDENING`. +- **"A proxy-set forward header is honoured without authentication."** + Honoured only if (a) `proxy.header.verify=true`, (b) the header is one of + `proxy.header.names`, *and* (c) the connecting `Remote_Addr` ∈ + `proxy.cidr` *(setting names maintainer: vishesh92)*. → `KNOWN-NON-FINDING`. +- **"Session-fixation: a session ID is reusable after failed login."** + `invalidateHttpSession` is called on each auth failure path per + `ApiServlet.java`. → `KNOWN-NON-FINDING` (verify the symptom; if + reproducible, escalate to `MODEL-GAP`). + +## §12 Conditions that would change this model + +Revise this document when any of the following lands: + +- A new authentication mechanism on a client-facing surface (e.g. + mTLS-as-API-auth on the JSON API, WebAuthn, OIDC). +- A new RBAC backend beyond the three included ACL plugins (e.g. OPA + integration, policy-engine integration). +- A new data-at-rest encryption story at the CloudStack layer (currently + delegated; see §9). +- A change in the default of any §5a flag, *especially* + `ca.plugin.root.auth.strictness` and `ca.plugin.root.allow.expired.cert`. +- Removal or change of the legacy `md5` / `plain-text` user-authenticator + plugins. +- A change in the signing algorithm or signature scheme on the JSON API + (e.g. SHA1 → SHA256 by default). +- A new hypervisor or system VM that adds a new trust boundary. +- A change in the extension mechanisms implemented by CloudStack + *(maintainer: DaanHoogland — §14 Q36)*. +- A new external-data surface (a new SDN controller integration, a new + storage provider, a new backup provider). +- A vulnerability report that cannot be cleanly routed to one of the §13 + dispositions: that is evidence the model is incomplete. + +## §13 Triage dispositions + +A report against `apache/cloudstack` receives exactly one of the +following: + +| Disposition | Meaning | Licensed by | +| --- | --- | --- | +| `VALID` | Violates a §8 property via an in-scope §7 adversary using an in-scope §6 input. | §8, §6, §7 | +| `VALID-HARDENING` | No §8 property violated, but a §11 misuse pattern can be made harder to fall into by code change. Fixed at maintainer discretion, typically no CVE. | §11 | +| `OUT-OF-MODEL: trusted-input` | Requires attacker control of a §6 parameter the model marks trusted (e.g. operator-supplied config flag, hostile LDAP/SAML/NSX/etc.). | §6 | +| `OUT-OF-MODEL: adversary-not-in-scope` | Requires a §7 actor the model excludes (operator, hostile hypervisor, hostile external IdP / SDN, Byzantine peer, side-channel observer, same-host non-`cloudstack` `root`). | §7 | +| `OUT-OF-MODEL: unsupported-component` | Lands in `tools/marvin/`, `test/`, `developer/`, `quickcloud/`, vendored upstream code, `simulator` hypervisor, etc. | §3 items 7–8 | +| `OUT-OF-MODEL: non-default-build` | Only manifests under a §5a flag that is not the new-setup default (e.g. `ca.plugin.root.auth.strictness=false` surviving an un-remediated pre-Aug-2017 upgrade, integration port `:8096` open). | §5a | +| `OUT-OF-MODEL: equivalent-harm` | An actor already-authorized under the model can cause the same harm via a documented path (root admin doing root-admin things, RBAC-licensed user using their RBAC-licensed commands). | §3 items 4, 5 | +| `BY-DESIGN: property-disclaimed` | Concerns a §9 property the project explicitly does not provide (template sandboxing, side-channel resistance, hypervisor isolation, etc.). | §9 | +| `KNOWN-NON-FINDING` | Matches a §11a recurring false positive. | §11a | +| `MODEL-GAP` | Cannot be cleanly routed to any of the above — triggers §12 model revision. | §12 | + +## §14 Open questions for the maintainers + +Every *(inferred)* tag in the body maps to one of these. Proposed answers +are inline; please confirm, correct, or strike. + +### Wave 1 — scope, intended use, the two big insecure defaults + +**Q1.** ~~The model assumes CloudStack is "a clustered distributed +control plane deployed inside an operator-controlled datacenter +network", not a single-host appliance or a hosted SaaS. Confirm?~~ +**RESOLVED** *(maintainer: DaanHoogland)* — distributed control plane; +**both** a single management-server instance (smaller clouds) and a +clustered deployment are supported topologies. Folded into §2. + +**Q2.** ~~Are the SecondaryStorageVM, ConsoleProxyVM, and Virtual Router +treated as trusted-once-enrolled peers, or do they get their own trust +tier?~~ **RESOLVED** *(maintainer)* — **yes**, same trust tier as agents, +not a separate tier. Folded into §2 caller-roles. + +**Q3.** ~~Are external integrations (LDAP, SAML2 IdP, OAuth2 IdP, NSX +controller, Netscaler, Tungsten, S3-compatible storage, backup +providers) modeled as trusted control-plane peers?~~ **RESOLVED** +*(maintainer: DaanHoogland — yes)* — trusted control-plane peers; this +licenses §3 item 2 and §11a trusted-input dispositions. *(maps to §2, §3, §11a)* + +**Q4.** ~~SecondaryStorageVM HTTP download surface — is the URL token +per-template ACL-checked, or is the SSVM URL itself a bearer credential?~~ +**RESOLVED** *(maintainer: vishesh92, DaanHoogland)* — download links are +UUID-named symlinks served by an Apache httpd with **no auth on the link**; +the UUID format defeats enumeration and the symlink is removed after a +period (timed availability is the mitigation). The PMC noted this should +be re-tested/confirmed in code. Folded into §6, §11a. *(Daan also asked +why static code analysis did not surface this — a note for the scan +agent, not a model gap.)* + +**Q5.** ~~Vendored upstream code under `systemvm/agent/noVNC` and bundled +JaSypt / Bouncy Castle / JSch — is the policy "report upstream; we pick up +fixes on next sync"?~~ **RESOLVED** *(maintainer: vishesh92, DaanHoogland)* +— `systemvm/agent/noVNC` is a **vendored fork of `github.com/novnc/novnc`** +with CloudStack changes; vendored bugs go upstream. There is **no automated +update procedure today** (dependabot has not produced viable PRs); the PMC +would prefer to establish one. Folded into §3 item 8, §11a. + +**Q6.** ~~Is "an operator with `root` on a management-server host, the +JCEKS keystore + encryption keys, the Root CA private key, or MariaDB +credentials" out of scope?~~ **RESOLVED** *(maintainer: DaanHoogland — yes)* +— `OUT-OF-MODEL: adversary-not-in-scope`. *(maps to §3 item 1, §9)* + +**Q7.** ~~Hypervisor bugs (libvirt / vSphere SDK / XenAPI / Hyper-V API / +KVM/QEMU itself) — out of scope, report upstream?~~ **RESOLVED** +*(maintainer: DaanHoogland — yes, out of scope; report upstream)*. *(maps to §3 item 3)* + +### Wave 2 — the two big insecure defaults + +**Q12.** ~~**Highest-leverage question in the model.** Are +`ca.plugin.root.auth.strictness` and `ca.plugin.root.allow.expired.cert` +shipped insecure-by-default?~~ **RESOLVED** *(maintainer: vishesh92, +DaanHoogland — `https://github.com/apache/cloudstack/pull/2239`)*: + +- `ca.plugin.root.auth.strictness` defaults to **`true` on new setups** — + the management server **does** require a Root-CA-signed client cert on + `:8250` and the cluster ports. It is `false` **only** after upgrading + from a version released before Aug 2017 that predates the setting; the + upgrade instructions document turning it on, so a leftover `false` is an + upgrade-hygiene gap, not a shipped insecure default. +- `ca.plugin.root.allow.expired.cert` defaults to `true` as an operational + concession to cert-rotation lag. + +This resolution reshaped §3 item 1, §5a, §7 (the un-certed peer row), +§8 P5, §9 false-friends, §10, §11, §11a, and §13. The earlier +"assumes operator must flip per §10" framing is withdrawn. + +### Wave 3 — adjacent insecure defaults and admin-only surfaces + +**Q8.** ~~Is "a root admin with full RBAC role causes harm Y via a +documented path Z" out of scope (proposed: **yes**, `OUT-OF-MODEL: +equivalent-harm`)? In particular: `runCustomAction`, template upload, +plugin registration, global config change, system-VM patching, system-VM +console access.~~ **RESOLVED** *(maintainer: vishesh92)* — yes; a root +admin generally has direct access to most of these resources anyway → +`OUT-OF-MODEL: equivalent-harm`. *(maps to §3 item 4, §9)* + +**Q9.** ~~Guest VM workloads — confirm that hypervisor-mediated side +channels and resource-exhaustion-within-allocation are out of scope, and +that the in-scope orchestration concerns are limited to "did CloudStack +place the VM in the right VLAN / apply the right security group / route +the right IP" (proposed)?~~ **RESOLVED** *(maintainer: vishesh92)* — yes; +side channels + resource-exhaustion-within-allocation are out of scope. +The one in-model case: CloudStack applying a wrong/insecure setting while +launching or managing the guest (CloudStack must use correct/secure +hypervisor settings). *(DaanHoogland to confirm the boundary.)* *(maps to +§3 item 5, §7, §9)* + +**Q10.** ~~Templates / ISOs / user-data — confirm that there is no +sandboxing of user-supplied OS images, and that user-data is intentionally +a code-execution channel into the guest (proposed)?~~ **RESOLVED** +*(maintainer: vishesh92)* — yes; userdata is the end user customizing +their own guest OS (tenant-controlled data inside their own boundary), not +a CloudStack-side injection surface. *(maps to §3 item 6, §9)* + +**Q11.** Confirm the unsupported-component list: `tools/marvin/`, +`test/`, `developer/`, `quickcloud/`, `cloud-cli/`, +`tools/{devcloud4,devcloud-kvm,appliance,checkstyle,transifex,bugs-wiki,...}`, +`simulator` hypervisor plugin. Anything to add or remove? **RESOLVED** *(maintainer: DaanHoogland)* — exclude `simulator` and `tools/appliance` explicitly (out of scope for now; a future security-purpose tooling effort may revisit). *(maps to §3 item 7)* + +**Q17.** Forward-header gating — the **setting names are confirmed** +*(maintainer: vishesh92)*: `proxy.header.verify` (the on/off gate), +`proxy.header.names` (header names to consult), and `proxy.cidr` (CIDRs of +the `Remote_Addr` values for which those headers are honoured). **RESOLVED** *(maintainer: vishesh92)* — `proxy.header.verify` is +**`false` by default**; only when the connecting `Remote_Addr` ∈ +`proxy.cidr` does CloudStack read the client IP from `proxy.header.names`. +*(maps to §5a, §6, §10)* + +**Q18.** ~~2FA — proposed: off by default, operator turns it on per +domain / per user via `enable.2fa.*`. Confirm; and is "2FA disabled in +production" a §10 violation or a deployment choice?~~ **RESOLVED** +*(maintainer: vishesh92)* — deployment choice, not a §10 violation. Two +domain-configurable global settings: `enable.user.2fa` (default `false`; +whether 2FA is enabled) and `mandate.user.2fa` (default `false`; whether +2FA is mandatory — applies only when `enable.user.2fa` is true). *(maps to +§5a, §10)* + +**Q19.** User-authenticator plugins — encoder selection is governed by +`user.password.encoders.order` (default +`PBKDF2,SHA256SALT,MD5,LDAP,SAML2,PLAINTEXT`) and +`user.password.encoders.exclude` (default `MD5,LDAP,PLAINTEXT`), so PBKDF2 +is the effective hashing default and `MD5`/`PLAINTEXT` are retained only +for verifying legacy hashes *(maintainer: vishesh92)*. **RESOLVED** *(maintainer: vishesh92)* — a +report against `md5`/`plain-text` hashing *new* passwords in a greenfield +install is `OUT-OF-MODEL: non-default-build`: the default +`user.password.encoders.exclude` (`MD5,LDAP,PLAINTEXT`) removes them from +the effective set, so the supported greenfield encoders are +`PBKDF2,SHA256SALT,SAML2`. *(maps to §5a, §10, §11)* + +**Q20.** Integration API port `:8096` — proposed: closed (port-zero) by +default in production packaging, open only when explicitly configured; +when open, it is unauthenticated by design. A report of "integration +port allows admin commands without auth" is `OUT-OF-MODEL: +non-default-build` *if* the operator opened it, else `VALID`. Confirm the default. **RESOLVED** *(maintainer: DaanHoogland)* — default is `0` (disabled); `8096` is set only in test configurations. *(maps to §5a, §10, §11a)* + +### Wave 4 — environment, distributed model, false-friends + +**Q13.** Network-fabric assumptions — proposed: at least four logical +networks (management, public, guest, storage), with the management +network as the trusted control plane. Is that the canonical model, or +do you support more compressed topologies (single-fabric) in production? **RESOLVED** *(maintainer: DaanHoogland)* — there are four logical networks (management, public, guest, storage); each may have multiple instances across topologies (e.g. multiple zones) and may be combined within physical networks, but all four logical types must be present for a functional system. *(maps to §5, §10)* + +**Q14.** Clock-skew assumption for signature v3 `expires` enforcement — +proposed: operator's responsibility to keep client + management-server clocks roughly in sync. Confirm. **RESOLVED** *(maintainer: DaanHoogland)* — confirmed; operator responsibility (PMC to add to the security model page). *(maps to §5)* + +**Q15.** Confirm the filesystem-permissions inventory for sensitive +files: JCEKS keystore, Root CA private key, JaSypt key + IV, +`db.properties`. Who owns them, what mode? **RESOLVED** *(maintainer: vishesh92)* — not a CSV inventory of every file in a running system; only the four sensitive artifacts named here (JCEKS keystore, Root CA private key, JaSypt key + IV, `db.properties`). Ownership is `root` (user) / `cloud` (group), mode read+write for the owner and read for the `cloud` group (i.e. `0640`, `root:cloud`). *(maps to §5, §10)* + +**Q16.** Confirm the "what CloudStack does not do to its host" inventory +in §5: no child processes besides agent `Script` invocations / system +VM provisioning; signal-handlers via servlet container default; +environment-variable consumption confined to documented set. Anything to add? **RESOLVED** *(maintainer: DaanHoogland)* — confirmed; nothing to add. *(maps to §5)* + +**Q21.** API request size cap and cluster/agent RPC payload size cap — +are these explicitly bounded, or "whatever Jetty / NIO defaults give"? **RESOLVED** *(maintainer: DaanHoogland)* — the UI server sets an explicit cap, `org.apache.cloudstack.ServerDaemon.DEFAULT_REQUEST_CONTENT_SIZE = 1048576` (1 MiB); for other components the sizes are capped by the upstream components used. *(maps to §6, §9)* + +**Q22.** `api.throttling.*` and per-account resource limits — proposed: +these are the entire DoS-protection surface, with no engine-level guard. Confirm. **RESOLVED** *(maintainer: DaanHoogland)* — confirmed; enforced at the API access check, and `api.throttling.enabled` is **`false` by default**. *(maps to §6, §9, §10)* + +**Q23.** Decompression behaviour on uploaded QCOW2 / RAW / OVA — proposed: +no engine-side cap; per-account storage limits + hypervisor limits are the bound. Confirm. **RESOLVED** *(maintainer: DaanHoogland)* — correct. *(maps to §6, §9)* + +**Q24.** Same-host non-`cloudstack` UID — proposed: game-over, no defence +claimed. Confirm. **RESOLVED** *(maintainer: DaanHoogland, vishesh92)* — same-host non-`cloudstack` UID is game-over (no defence claimed; an operator at that level already controls the host, §3). On the related host-registration question: re-adding a host with the same IP **updates the existing host record** rather than creating a spoofed peer, and is gated by root-admin/operator access plus the keys/certs required to connect to the management server — adding a host directly without those fails (refinement tracked in apache/cloudstack#13182). So this is not an unauthenticated identity-spoof path. *(maps to §7, §9)* + +**Q25.** Side-channel observers (CPU cache timing, branch-predictor / speculative-execution channels e.g. Spectre-class, hypervisor-shared microarchitectural channels) — out of scope (proposed). **RESOLVED** *(maintainer: DaanHoogland)* — agreed, out of scope. *("branch" = branch-predictor / speculative-execution side channels — clarified by producer.)* *(maps to §7, §9)* + +**Q26.** Byzantine-internal-peer threshold — confirm CloudStack makes no +BFT claim, so any compromised cluster peer or agent with a valid +Root-CA-issued cert is unbounded (proposed). **RESOLVED** *(maintainer: DaanHoogland)* — agreed; no BFT claim. (A quorum-style mitigation would only be meaningful in larger clusters, not single/dual-node — possible future feature proposal.) *(maps to §7, §9)* + +**Q27.** §8 P9 memory-safety — JVM-bounded; is the reachability +boundary correctly "in-model for the JSON API + B5 input; out-of-model +for native hypervisor SDK bugs that surface as `Throwable`"? **RESOLVED** *(maintainer: DaanHoogland)* — the reachability boundary is right, but **§8 P9 must not imply CloudStack is Java-only** — no implementation-language limitation is presumed (ocaml, python, bash run on hypervisors; go is used on the management server; the set may grow). The memory-safety claims hold for the JVM components only. *(reflected in §8 P9.)* *(maps to §8 P9, §9)* + +**Q28.** §8 P10 listing-scope — confirm the §10 invariant "`list*` +responses are scoped to the principal's domain/account/project". And: +is information leak via error messages / async-job status / event log an in-model concern, or accepted? **RESOLVED** *(maintainer: DaanHoogland)* — in-model: regular system logs (e.g. log4j) are exempt, but other than those, information leaks (via error messages, async-job status, event log) are a concern. *(maps to §8 P10, §9, §11)* + +**Q29.** Data-at-rest encryption — confirm CloudStack delegates entirely +to storage layer / hypervisor (LUKS, Ceph encryption, vSphere VM +Encryption); no CloudStack-layer encryption of guest volumes. **RESOLVED** *(maintainer: DaanHoogland, vishesh92)* — correct; delegated entirely to the storage layer / hypervisor. *(maps to §9)* + +**Q30.** Constant-time comparison — confirm that *only* the API +signature path uses `ConstantTimeComparator`. Login password compare, +session cookie compare, console-token compare — none documented +constant-time. Is that intentional? **RESOLVED** *(maintainer: DaanHoogland)* — not intentional — the absence of constant-time comparison on the login-password / session-cookie / console-token paths is a lack of feature (hardening opportunity), not a by-design decision. *(maps to §8, §9)* + +**Q31.** Time-of-check-to-time-of-use between RBAC check at API entry +and orchestration on agent fleet — confirm mid-job RBAC revocation is +**not** retroactively enforced (proposed). **RESOLVED** *(maintainer: DaanHoogland)* — agreed/confirmed. *(maps to §9)* + +**Q32.** TLS posture on `:8080` vs `:8443` — confirm production deploys +behind TLS on `:8443` or behind a TLS-terminating reverse proxy; a bare +`:8080` HTTP API is dev-only. **RESOLVED** *(maintainer: DaanHoogland)* — confirmed. *(maps to §5a, §10)* + +**Q33.** `security.encryption.key` reuse across environments — confirm +that reusing the JaSypt key + IV across staging and production is a +documented misuse. **RESOLVED** *(maintainer: DaanHoogland)* — indeed — confirmed misuse. *(maps to §11)* + +### Wave 5 — meta + +**Q34.** Should this document live at `docs/threat-model.md` in +`apache/cloudstack`, or as a page on `cloudstack.apache.org/security/`? +Or both, with one canonical and the other linked? **RESOLVED** *(maintainer: DaanHoogland, vishesh92)* — both: this document is the source of truth, and `cloudstack.apache.org/security` carries an excerpt plus a link to it. *(meta)* + +**Q35.** Is there an existing CloudStack threat-model document +(Confluence, internal, or a `[SECURITY]`-tagged dev@ thread) that this +should reconcile against rather than supersede? **RESOLVED** *(maintainer: DaanHoogland)* — `cloudstack.apache.org/security/` is the only existing security model today; this document becomes its source of truth, with the page linking to it. *(meta — §3.1a of the rubric)* + +**Q36.** What kind of change should trigger a revision (proposed list in +§12 — confirm or correct)? **RESOLVED** *(maintainer: DaanHoogland)* — confirmed, plus add: a change in the extension mechanisms implemented by CloudStack (now reflected in §12). *(meta, §12)* + +**Q37.** §11a is the highest-leverage section for the scan agent's +suppression list. The current draft has 15 patterns; could the PMC +populate §11a from recurring "not a vuln" closures on the +`security@apache.org` ↔ CloudStack triage queue and on +`https://cloudstack.apache.org/security.html`? Concrete asks: 3–5 +patterns the PMC sees recur in inbound reports (e.g. "SSL bare on +`:8080` in a dev cluster", "agent port open without strictness flipped", +"`md5` authenticator left enabled after upgrade", "console URL appears +in support ticket"). *(meta — §11a)* + +**Q38.** Confirm the structural decision to keep the four satellite repos +as separate delta models (`cloudstack-go-threat-model-draft.md`, +`cloudstack-cloudmonkey-threat-model-draft.md`, +`cloudstack-terraform-provider-threat-model-draft.md`, +`cloudstack-kubernetes-provider-threat-model-draft.md`) inheriting §3 +/ §4 / §7 from this document. **RESOLVED** *(maintainer: DaanHoogland)* — confirmed; the satellites are not the system core (the core runs without them, they cannot run without the core), and there is an added hierarchy — `cloudstack-go` is a dependency of the other three. *(meta, §3 item 9)* + +--- + +## Appendix: SECURITY.md → §x back-map + +CloudStack does not currently ship an in-repo `SECURITY.md`; the `README.md` +section "Reporting Security Vulnerabilities" points to +`https://cloudstack.apache.org/security.html` as the canonical disclosure +landing page. The de facto security-policy artifacts are scattered: + +| Source | Claim | Lands in | +| --- | --- | --- | +| `README.md` "Reporting Security Vulnerabilities" | report to `security@apache.org`; canonical page at `cloudstack.apache.org/security.html` | §1 reporting cross-reference | +| `README.md` "Notice of Cryptographic Software" | JaSypt, Bouncy Castle, JSch, OpenSwan, MySQL native encryption | §5 cryptography assumption, §8 P8 | +| `agent/conf/agent.properties` (`host`, `port`, `ssl.handshake.timeout`, …) | agent ↔ management server transport on `:8250` | §2 component table, §4 B5 | +| `server/src/main/java/com/cloud/api/ApiServer.java` `verifyRequest` (lines ~980–1156) | HMAC-SHA1 signature + `expires` enforcement (`enforce.post.requests.and.timestamps`) + constant-time compare | §8 P1, §8 P3, §5a "enforce.post.requests.and.timestamps" row, §11a "SHA1 / constant-time" entries | +| `server/src/main/java/com/cloud/api/ApiServlet.java` `getClientAddress` (lines 700–725) | forward-header gating by `proxy.cidr` / `proxy.header.names` when `proxy.header.verify=true` | §8 P6, §5a "proxy.header.verify" row | +| `server/src/main/java/com/cloud/api/ApiServlet.java` 2FA path (lines 360–582) | password + 2FA flow | §8 P2 | +| `framework/ca/.../CAService.java`, `plugins/ca/root-ca/.../RootCAProvider.java` | Root CA generated at first boot; agent enrolment via `SetupKeyStoreCommand` | §4 B5, §8 P5, §5a strictness/allow-expired rows | +| `plugins/ca/root-ca/.../RootCACustomTrustManager.java` | `authStrictness` and `allowExpiredCertificate` semantics | §5a, §8 P5 | +| `plugins/acl/{static,dynamic,project}-role-based` | RBAC backends | §8 P4 | +| `plugins/user-authenticators/{md5,sha256salted,pbkdf2,plain-text,ldap,saml2,oauth2}` | pluggable user auth; selection via `user.password.encoders.order` / `user.password.encoders.exclude` | §2 caller-roles row, §5a "user.password.encoders.*" rows, §10 item 7 | +| `plugins/user-two-factor-authenticators/{static-pin,totp}` | 2FA backends | §5a "enable.user.2fa / mandate.user.2fa", §10 item 8 | +| `framework/security/.../KeysManager.java`, `KeystoreManager.java` | `security.encryption.key`, `security.encryption.iv` (Hidden), application-secret JaSypt encryption | §8 P8, §5a, §10 item 6 | +| `agent/src/main/java/com/cloud/agent/Agent.java` `setupAgentKeystore` (lines ~793–916) | agent receives Root CA-signed cert via `SetupKeyStoreCommand` and imports it | §4 B5, §8 P5 | +| `server/src/main/java/com/cloud/servlet/ConsoleProxyServlet.java`, `ConsoleProxyPasswordBasedEncryptor.java` | signed encrypted console-proxy URL token | §4 B3, §8 P7 | +| `https://cloudstack.apache.org/security.html` (website) | canonical disclosure landing page | §1 reporting cross-reference (note: not accessible from the producer's network at draft time; verify content with PMC) | diff --git a/agent/bindir/cloud-setup-agent.in b/agent/bindir/cloud-setup-agent.in index 9927c22eebcf..dc4582540df9 100755 --- a/agent/bindir/cloud-setup-agent.in +++ b/agent/bindir/cloud-setup-agent.in @@ -35,7 +35,7 @@ for pythonpath in ( 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 @@ -43,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 @@ -79,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") @@ -100,7 +108,8 @@ def getUserInputs(): return [mgtSvr, zoneToken, network, podId, clusterId, hypervisor] -if __name__ == '__main__': + +if __name__ == "__main__": initLoging("@AGENTLOGDIR@/setup.log") glbEnv = globalEnv() @@ -108,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") @@ -140,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 @@ -168,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/conf/agent.properties b/agent/conf/agent.properties index 2a083de6fe53..4e36eff4d75f 100644 --- a/agent/conf/agent.properties +++ b/agent/conf/agent.properties @@ -78,6 +78,14 @@ zone=default # Generated with "uuidgen". local.storage.uuid= +# Enable TLS for image server transfers. The keys are read from: +# cert file = /etc/cloudstack/agent/cloud.crt +# key file = /etc/cloudstack/agent/cloud.key +image.server.tls.enabled=true + +# The Address for the network interface that the image server listens on. If not specified, it will listen on the Management network. +#image.server.listen.address= + # Location for KVM virtual router scripts. # The path defined in this property is relative to the directory "/usr/share/cloudstack-common/". domr.scripts.dir=scripts/network/domr/kvm @@ -308,7 +316,7 @@ iscsi.session.cleanup.enabled=false #vm.migrate.domain.retrieve.timeout=10 # This parameter specifies if the host must be rebooted when something goes wrong with the heartbeat. -#reboot.host.and.alert.management.on.heartbeat.timeout=true +#reboot.host.and.alert.management.on.heartbeat.timeout=false # Enables manually setting CPU's topology on KVM's VM. #enable.manually.setting.cpu.topology.on.kvm.vm=true @@ -364,6 +372,14 @@ iscsi.session.cleanup.enabled=false # to the directory "/usr/share/cloudstack-common/". #network.scripts.dir=scripts/vm/network/vnet +# Sets the VXLAN networking mode used, either 'multicast' (default) or 'evpn'. +# The different modes lead to different scripts being executed by the Agent. +# multicast: modifyvxlan.sh +# evpn: modifyvxlan-evpn.sh +# Existing environments using VXLAN can safely switch to the 'evpn' mode as this +# will not break existing functionality. +#network.vxlan.mode=multicast + # Defines the location for storage scripts. # The path defined in this property is relative. # To locate the script, ACS first tries to concatenate @@ -451,3 +467,36 @@ iscsi.session.cleanup.enabled=false # If set to true, creates VMs as full clones of their templates on KVM hypervisor. Creates as linked clones otherwise. # create.full.clone=false + +# Instance conversion TMPDIR env var +#convert.instance.env.tmpdir= + +# Instance conversion VIRT_V2V_TMPDIR env var +#convert.instance.env.virtv2v.tmpdir= + +# Time, in seconds, to wait before retrying to rebase during the incremental snapshot process. +# incremental.snapshot.retry.rebase.wait=60 + +# Path to the VDDK library directory for VMware to KVM conversion via VDDK, +# passed to virt-v2v as -io vddk-libdir= +#vddk.lib.dir= + +# Ordered VDDK transport preference for VMware to KVM conversion via VDDK, passed as +# -io vddk-transports= to virt-v2v. Example: nbd:nbdssl +#vddk.transports= + +# Optional vCenter SHA1 thumbprint for VMware to KVM conversion via VDDK, passed as +# -io vddk-thumbprint=. If unset, CloudStack computes it on the KVM host via openssl. +#vddk.thumbprint= + +# Timeout (in seconds) for QCOW2 delta merge operations, mainly used for classic volume snapshots, disk-only VM snapshots on file-based storage, and the KBOSS plugin. +# If a value of 0 or less is informed, the default will be used. +# qcow2.delta.merge.timeout=259200 + +# Maximum number of backup validation jobs that can be executed at the same time. Values lower than 0 remove the limit, meaning that as many validations as possible will be done at +# the same time. +# backup.validation.max.concurrent.operations.per.host= + +# Maximum number of backup compression jobs that can be executed at the same time. Values lower than 0 remove the limit, meaning that as many compressions as possible will be +# done at the same time. +# backup.compression.max.concurrent.operations.per.host= diff --git a/agent/conf/developer.properties.template b/agent/conf/developer.properties.template index a70a136f38cd..02f51aa6bb85 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/conf/uefi.properties.in b/agent/conf/uefi.properties.in new file mode 100644 index 000000000000..3c8866f634bc --- /dev/null +++ b/agent/conf/uefi.properties.in @@ -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. + +# Configuration file for UEFI + +guest.nvram.template.legacy=@GUESTNVRAMTEMPLATELEGACY@ +guest.loader.legacy=@GUESTLOADERLEGACY@ +guest.nvram.template.secure=@GUESTNVRAMTEMPLATESECURE@ +guest.loader.secure=@GUESTLOADERSECURE@ +guest.nvram.path=@GUESTNVRAMPATH@ diff --git a/agent/pom.xml b/agent/pom.xml index 0fd7d35823e2..5ab6cfe17c13 100644 --- a/agent/pom.xml +++ b/agent/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.22.0.0-SNAPSHOT + 4.23.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 e86557e96817..275fd41edc34 100644 --- a/agent/src/main/java/com/cloud/agent/Agent.java +++ b/agent/src/main/java/com/cloud/agent/Agent.java @@ -613,7 +613,7 @@ protected void setupStartupCommand(final StartupCommand startup) { } protected String getAgentArch() { - String arch = Script.runSimpleBashScript(Script.getExecutableAbsolutePath("arch"), 1000); + String arch = Script.runSimpleBashScript(Script.getExecutableAbsolutePath("arch"), 2000); logger.debug("Arch for agent: {} found: {}", _name, arch); return arch; } @@ -1322,7 +1322,6 @@ public void doTask(final Task task) throws TaskExecutionException { processResponse((Response)request, task.getLink()); } else { //put the requests from mgt server into another thread pool, as the request may take a longer time to finish. Don't block the NIO main thread pool - //processRequest(request, task.getLink()); requestHandler.submit(new AgentRequestHandler(getType(), getLink(), request)); } } catch (final ClassNotFoundException e) { @@ -1332,13 +1331,14 @@ public void doTask(final Task task) throws TaskExecutionException { } } else if (task.getType() == Task.Type.DISCONNECT) { try { - // an issue has been found if reconnect immediately after disconnecting. please refer to https://github.com/apache/cloudstack/issues/8517 + // an issue has been found if reconnect immediately after disconnecting. // wait 5 seconds before reconnecting + logger.debug("Wait for 5 secs before reconnecting, disconnect task - {}", () -> getLinkLog(task.getLink())); Thread.sleep(5000); } catch (InterruptedException e) { } shell.setConnectionTransfer(false); - logger.debug("Executing disconnect task - {}", () -> getLinkLog(task.getLink())); + logger.debug("Executing disconnect task - {} and reconnecting", () -> getLinkLog(task.getLink())); reconnect(task.getLink()); } else if (task.getType() == Task.Type.OTHER) { processOtherTask(task); 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 57dc607b920b..e4775188d0ca 100644 --- a/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java +++ b/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java @@ -117,12 +117,26 @@ public class AgentProperties{ /** * Local storage path.
- * This property allows multiple values to be entered in a single String. The differente values must be separated by commas.
+ * This property allows multiple values to be entered in a single String. The different values must be separated by commas.
* Data type: String.
* Default value: /var/lib/libvirt/images/ */ public static final Property LOCAL_STORAGE_PATH = new Property<>("local.storage.path", "/var/lib/libvirt/images/"); + /** + * Enables TLS on the KVM image server transfer endpoint.
+ * Data type: Boolean.
+ * Default value: true + */ + public static final Property IMAGE_SERVER_TLS_ENABLED = new Property<>("image.server.tls.enabled", true); + + /** + * The IP address that the KVM image server listens on.
+ * Data type: String.
+ * Default value: null + */ + public static final Property IMAGE_SERVER_LISTEN_ADDRESS = new Property<>("image.server.listen.address", null, String.class); + /** * Directory where Qemu sockets are placed.
* These sockets are for the Qemu Guest Agent and SSVM provisioning.
@@ -134,7 +148,7 @@ public class AgentProperties{ /** * MANDATORY: The UUID for the local storage pool.
- * This property allows multiple values to be entered in a single String. The differente values must be separated by commas.
+ * This property allows multiple values to be entered in a single String. The different values must be separated by commas.
* Data type: String.
* Default value: null */ @@ -156,7 +170,8 @@ 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.
+ * The timeout (in seconds) for QCOW2 delta merge operations, mainly used for classic volume snapshots, disk-only VM snapshots on file-based storage, and the KBOSS plugin. + * If a value of 0 or less is informed, the default will be used.
* This configuration is only considered if libvirt.events.enabled is also true.
* Data type: Integer.
* Default value: 259200 @@ -304,6 +319,15 @@ public class AgentProperties{ */ public static final Property NETWORK_BRIDGE_TYPE = new Property<>("network.bridge.type", "native"); + /** + * Sets the VXLAN networking mode used by the BridgeVifDriver.
+ * Possible values: multicast | evpn
+ * When set to evpn, the driver will use modifyvxlan-evpn.sh instead of modifyvxlan.sh.
+ * Data type: String.
+ * Default value: multicast + */ + public static final Property NETWORK_VXLAN_MODE = new Property<>("network.vxlan.mode", "multicast"); + /** * Sets the driver used to plug and unplug NICs from the bridges.
* A sensible default value will be selected based on the network.bridge.type but can be overridden here.
@@ -593,10 +617,10 @@ public class AgentProperties{ /** * This parameter specifies if the host must be rebooted when something goes wrong with the heartbeat.
* Data type: Boolean.
- * Default value: true + * Default value: false */ public static final Property REBOOT_HOST_AND_ALERT_MANAGEMENT_ON_HEARTBEAT_TIMEOUT - = new Property<>("reboot.host.and.alert.management.on.heartbeat.timeout", true); + = new Property<>("reboot.host.and.alert.management.on.heartbeat.timeout", false); /** * Enables manually setting CPU's topology on KVM's VM.
@@ -794,6 +818,44 @@ public Property getWorkers() { */ public static final Property VIRTV2V_VERBOSE_ENABLED = new Property<>("virtv2v.verbose.enabled", false); + /** + * Set env TMPDIR var for virt-v2v Instance Conversion from VMware to KVM + * Data type: String.
+ * Default value: null + */ + public static final Property CONVERT_ENV_TMPDIR = new Property<>("convert.instance.env.tmpdir", null, String.class); + + /** + * Set env VIRT_V2V_TMPDIR var for virt-v2v Instance Conversion from VMware to KVM + * Data type: String.
+ * Default value: null + */ + public static final Property CONVERT_ENV_VIRTV2V_TMPDIR = new Property<>("convert.instance.env.virtv2v.tmpdir", null, String.class); + + /** + * Path to the VDDK library directory on the KVM conversion host, used when converting VMs from VMware to KVM via VDDK. + * This directory is passed to virt-v2v as -io vddk-libdir=<path>. + * Data type: String.
+ * Default value: null + */ + public static final Property VDDK_LIB_DIR = new Property<>("vddk.lib.dir", null, String.class); + + /** + * Ordered list of VDDK transports for virt-v2v, passed as -io vddk-transports=<value>. + * Example: nbd:nbdssl. + * Data type: String.
+ * Default value: null + */ + public static final Property VDDK_TRANSPORTS = new Property<>("vddk.transports", null, String.class); + + /** + * vCenter TLS certificate thumbprint used by virt-v2v VDDK mode, passed as -io vddk-thumbprint=<value>. + * If unset, the KVM host computes it at runtime from the vCenter endpoint. + * Data type: String.
+ * Default value: null + */ + public static final Property VDDK_THUMBPRINT = new Property<>("vddk.thumbprint", null, String.class); + /** * BGP controll CIDR * Data type: String.
@@ -871,6 +933,38 @@ public Property getWorkers() { */ public static final Property CREATE_FULL_CLONE = new Property<>("create.full.clone", false); + /** + * Time, in seconds, to wait before retrying to rebase during the incremental snapshot process. + * */ + public static final Property INCREMENTAL_SNAPSHOT_RETRY_REBASE_WAIT = new Property<>("incremental.snapshot.retry.rebase.wait", 60); + + /** + * When set to true, executes modifymacip.sh (resolved via the + * network scripts directory) on VM NIC plug (VM start) and unplug (VM stop) to manage static + * ARP/NDP entries and host routes for VM interfaces.
+ * The script is invoked with:
+ *   add: -o add -b <bridge> -m <mac> [-4 <ipv4>] [-6 <ipv6>]
+ *   delete: -o delete -b <bridge> -m <mac>
+ * A bundled reference implementation is available at + * scripts/vm/network/vnet/modifymacip.sh.
+ * Set to false or leave unset to disable this feature.
+ * Data type: Boolean.
+ * Default value: false + */ + public static final Property VM_NETWORK_MACIP_STATIC = new Property<>("vm.network.macip.static", false, Boolean.class); + + + /** + * Maximum number of backup validation jobs that can be executed at the same time. Values lower than 0 remove the limit, meaning that as many validations as possible will be done at + * the same time. + */ + public static final Property BACKUP_VALIDATION_MAX_CONCURRENT_OPERATIONS_PER_HOST = new Property<>("backup.validation.max.concurrent.operations.per.host", null, Integer.class); + + /** + * Maximum number of backup compression jobs that can be executed at the same time. Values lower than 0 remove the limit, meaning that as many compressions as possible will be + * done at the same time. + */ + public static final Property BACKUP_COMPRESSION_MAX_CONCURRENT_OPERATIONS_PER_HOST = new Property<>("backup.compression.max.concurrent.operations.per.host", null, Integer.class); public static class Property { private String name; diff --git a/agent/src/main/java/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java b/agent/src/main/java/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java index 26f9d4b3d732..ef98fa532ecb 100644 --- a/agent/src/main/java/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java +++ b/agent/src/main/java/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java @@ -175,12 +175,12 @@ private Answer executeProxyLoadScan(final Command cmd, final long proxyVmId, fin try { is.close(); } catch (final IOException e) { - logger.warn("Exception when closing , console proxy address : {}", proxyManagementIp); + logger.warn("Exception when closing , console proxy address: {}", proxyManagementIp); success = false; } } } catch (final IOException e) { - logger.warn("Unable to open console proxy command port url, console proxy address : {}", proxyManagementIp); + logger.warn("Unable to open console proxy command port url, console proxy address: {}", proxyManagementIp); success = false; } @@ -331,7 +331,7 @@ private void launchConsoleProxy(final byte[] ksBits, final String ksPassword, fi final Object resource = this; logger.info("Building class loader for com.cloud.consoleproxy.ConsoleProxy"); if (consoleProxyMain == null) { - logger.info("Running com.cloud.consoleproxy.ConsoleProxy with encryptor password={}", encryptorPassword); + logger.info("Running com.cloud.consoleproxy.ConsoleProxy"); consoleProxyMain = new Thread(new ManagedContextRunnable() { @Override protected void runInContext() { diff --git a/api/pom.xml b/api/pom.xml index 6270eb2dead6..4cdb57b6414c 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.22.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT @@ -71,6 +71,11 @@ cloud-framework-direct-download ${project.version} + + org.apache.cloudstack + cloud-framework-kms + ${project.version} + diff --git a/api/src/main/java/com/cloud/agent/api/storage/OVFHelper.java b/api/src/main/java/com/cloud/agent/api/storage/OVFHelper.java index 6396e3deb723..23167c5c53b0 100644 --- a/api/src/main/java/com/cloud/agent/api/storage/OVFHelper.java +++ b/api/src/main/java/com/cloud/agent/api/storage/OVFHelper.java @@ -119,8 +119,7 @@ protected OVFPropertyTO createOVFPropertyFromNode(Node node, int index, String c boolean password = StringUtils.isNotBlank(passStr) && passStr.equalsIgnoreCase("true"); String label = ovfParser.getChildNodeValue(node, "Label"); String description = ovfParser.getChildNodeValue(node, "Description"); - logger.debug("Creating OVF property index " + index + (category == null ? "" : " for category " + category) - + " with key = " + key); + logger.debug("Creating OVF property index {} {} with key = {}", index, (category == null ? "" : " for category " + category), key); return new OVFPropertyTO(key, type, value, qualifiers, userConfigurable, label, description, password, index, category); } @@ -152,7 +151,7 @@ public List getConfigurableOVFPropertiesFromDocument(Document doc if (child.getNodeName().equalsIgnoreCase("Category") || child.getNodeName().endsWith(":Category")) { lastCategoryFound = child.getTextContent(); - logger.info("Category found " + lastCategoryFound); + logger.info("Category found {}", lastCategoryFound); } else if (child.getNodeName().equalsIgnoreCase("Property") || child.getNodeName().endsWith(":Property")) { OVFPropertyTO prop = createOVFPropertyFromNode(child, propertyIndex, lastCategoryFound); @@ -250,13 +249,13 @@ private List matchHardwareItemsToDiskAndFilesInformation(List extractDisksFromOvfDocumentTree(Document doc) { od._controller = getControllerType(items, od._diskId); vd.add(od); } - if (logger.isTraceEnabled()) { - logger.trace(String.format("found %d disk definitions",vd.size())); - } + logger.trace("Found {} disk definitions", vd.size()); return vd; } @@ -366,9 +363,7 @@ protected List extractFilesFromOvfDocumentTree(File ovfFile, Document d vf.add(of); } } - if (logger.isTraceEnabled()) { - logger.trace(String.format("found %d file definitions in %s",vf.size(), ovfFile.getPath())); - } + logger.trace("Found {} file definitions in {}", vf.size(), ovfFile.getPath()); return vf; } @@ -506,7 +501,7 @@ private void writeDocumentToFile(String newOvfFilePath, Document doc) { outfile.write(writer.toString()); outfile.close(); } catch (IOException | TransformerException e) { - logger.info("Unexpected exception caught while rewriting OVF:" + e.getMessage(), e); + logger.info("Unexpected exception caught while rewriting OVF: {}", e.getMessage(), e); throw new CloudRuntimeException(e); } } @@ -522,9 +517,7 @@ OVFFile getFileDefinitionFromDiskDefinition(String fileRef, List files) public List getNetPrerequisitesFromDocument(Document doc) throws InternalErrorException { if (doc == null) { - if (logger.isTraceEnabled()) { - logger.trace("no document to parse; returning no prerequisite networks"); - } + logger.trace("No document to parse; returning no prerequisite networks"); return Collections.emptyList(); } @@ -540,9 +533,7 @@ public List getNetPrerequisitesFromDocument(Document doc) throws I private void matchNicsToNets(Map nets, Node systemElement) { final DocumentTraversal traversal = (DocumentTraversal) systemElement; final NodeIterator iterator = traversal.createNodeIterator(systemElement, NodeFilter.SHOW_ELEMENT, null, true); - if (logger.isTraceEnabled()) { - logger.trace(String.format("starting out with %d network-prerequisites, parsing hardware",nets.size())); - } + logger.trace("Starting out with {} network-prerequisites, parsing hardware", nets.size()); int nicCount = 0; for (Node n = iterator.nextNode(); n != null; n = iterator.nextNode()) { final Element e = (Element) n; @@ -550,9 +541,7 @@ private void matchNicsToNets(Map nets, Node systemElement) nicCount++; String name = e.getTextContent(); // should be in our nets if(nets.get(name) == null) { - if(logger.isInfoEnabled()) { - logger.info(String.format("found a nic definition without a network definition byname %s, adding it to the list.", name)); - } + logger.info("Found a NIC definition without a Network definition by name {}, adding it to the list.", name); nets.put(name, new OVFNetworkTO()); } OVFNetworkTO thisNet = nets.get(name); @@ -561,9 +550,7 @@ private void matchNicsToNets(Map nets, Node systemElement) } } } - if (logger.isTraceEnabled()) { - logger.trace(String.format("ending up with %d network-prerequisites, parsed %d nics", nets.size(), nicCount)); - } + logger.trace("Ending up with {} network-prerequisites, parsed {} nics", nets.size(), nicCount); } /** @@ -585,7 +572,7 @@ private void fillNicPrerequisites(OVFNetworkTO nic, Node parentNode) { int addressOnParent = Integer.parseInt(addressOnParentStr); nic.setAddressOnParent(addressOnParent); } catch (NumberFormatException e) { - logger.warn("Encountered element of type \"AddressOnParent\", that could not be parse to an integer number: " + addressOnParentStr); + logger.warn("Encountered element of type \"AddressOnParent\", that could not be parse to an integer number: {}", addressOnParentStr); } boolean automaticAllocation = StringUtils.isNotBlank(automaticAllocationStr) && Boolean.parseBoolean(automaticAllocationStr); @@ -597,7 +584,7 @@ private void fillNicPrerequisites(OVFNetworkTO nic, Node parentNode) { int instanceId = Integer.parseInt(instanceIdStr); nic.setInstanceID(instanceId); } catch (NumberFormatException e) { - logger.warn("Encountered element of type \"InstanceID\", that could not be parse to an integer number: " + instanceIdStr); + logger.warn("Encountered element of type \"InstanceID\", that could not be parse to an integer number: {}", instanceIdStr); } nic.setResourceSubType(resourceSubType); @@ -630,9 +617,7 @@ private Map getNetworksFromDocumentTree(Document doc) { nets.put(networkName,network); } - if (logger.isTraceEnabled()) { - logger.trace(String.format("found %d networks in template", nets.size())); - } + logger.trace("Found {} Networks in Template", nets.size()); return nets; } @@ -771,7 +756,7 @@ private Long getLongValueFromString(String value) { try { return Long.parseLong(value); } catch (NumberFormatException e) { - logger.debug("Could not parse the value: " + value + ", ignoring it"); + logger.debug("Could not parse the value: {}, ignoring it", value); } } return null; @@ -782,7 +767,7 @@ private Integer getIntValueFromString(String value) { try { return Integer.parseInt(value); } catch (NumberFormatException e) { - logger.debug("Could not parse the value: " + value + ", ignoring it"); + logger.debug("Could not parse the value: {}, ignoring it", value); } } return null; @@ -820,7 +805,7 @@ public List getEulaSectionsFromDocument(Document doc) { try { compressedLicense = compressOVFEula(eulaLicense); } catch (IOException e) { - logger.error("Could not compress the license for info " + eulaInfo); + logger.error("Could not compress the license for info {}", eulaInfo); continue; } OVFEulaSectionTO eula = new OVFEulaSectionTO(eulaInfo, compressedLicense, eulaIndex); diff --git a/api/src/main/java/com/cloud/agent/api/storage/OVFParser.java b/api/src/main/java/com/cloud/agent/api/storage/OVFParser.java index 38f478d63cf8..316ab4ea87b9 100644 --- a/api/src/main/java/com/cloud/agent/api/storage/OVFParser.java +++ b/api/src/main/java/com/cloud/agent/api/storage/OVFParser.java @@ -54,7 +54,7 @@ public OVFParser() { documentBuilderFactory.setNamespaceAware(true); documentBuilder = documentBuilderFactory.newDocumentBuilder(); } catch (ParserConfigurationException e) { - logger.error("Cannot start the OVF parser: " + e.getMessage(), e); + logger.error("Cannot start the OVF parser: {}", e.getMessage(), e); } } @@ -70,7 +70,7 @@ public Document parseOVFFile(String ovfFilePath) { try { return documentBuilder.parse(new File(ovfFilePath)); } catch (SAXException | IOException e) { - logger.error("Error parsing " + ovfFilePath + " " + e.getMessage(), e); + logger.error("Error parsing {} {}", ovfFilePath, e.getMessage(), e); return null; } } diff --git a/api/src/main/java/com/cloud/agent/api/to/BucketTO.java b/api/src/main/java/com/cloud/agent/api/to/BucketTO.java index f7e4bfea80fb..fd8237998a74 100644 --- a/api/src/main/java/com/cloud/agent/api/to/BucketTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/BucketTO.java @@ -26,10 +26,13 @@ public final class BucketTO { private String secretKey; + private long accountId; + public BucketTO(Bucket bucket) { this.name = bucket.getName(); this.accessKey = bucket.getAccessKey(); this.secretKey = bucket.getSecretKey(); + this.accountId = bucket.getAccountId(); } public BucketTO(String name) { @@ -47,4 +50,8 @@ public String getAccessKey() { public String getSecretKey() { return this.secretKey; } + + public long getAccountId() { + return this.accountId; + } } diff --git a/api/src/main/java/com/cloud/agent/api/to/DataObjectType.java b/api/src/main/java/com/cloud/agent/api/to/DataObjectType.java index 26294cfbb223..76a75e03ba55 100644 --- a/api/src/main/java/com/cloud/agent/api/to/DataObjectType.java +++ b/api/src/main/java/com/cloud/agent/api/to/DataObjectType.java @@ -19,5 +19,5 @@ package com.cloud.agent.api.to; public enum DataObjectType { - VOLUME, SNAPSHOT, TEMPLATE, ARCHIVE + VOLUME, SNAPSHOT, TEMPLATE, ARCHIVE, BACKUP } 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 ca95fcfd6790..2ed7d9f9a201 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 @@ -33,6 +33,7 @@ public class NicTO extends NetworkTO { boolean dpdkEnabled; Integer mtu; Long networkId; + boolean enabled; String networkSegmentName; @@ -154,4 +155,12 @@ public String getNetworkSegmentName() { public void setNetworkSegmentName(String networkSegmentName) { this.networkSegmentName = networkSegmentName; } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } } 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 18737c584b34..7daeb9649177 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 @@ -36,13 +36,17 @@ public class RemoteInstanceTO implements Serializable { private String vcenterPassword; private String vcenterHost; private String datacenterName; + private String clusterName; + private String hostName; public RemoteInstanceTO() { } - public RemoteInstanceTO(String instanceName) { + public RemoteInstanceTO(String instanceName, String clusterName, String hostName) { this.hypervisorType = Hypervisor.HypervisorType.VMware; this.instanceName = instanceName; + this.clusterName = clusterName; + this.hostName = hostName; } public RemoteInstanceTO(String instanceName, String instancePath, String vcenterHost, String vcenterUsername, String vcenterPassword, String datacenterName) { @@ -55,6 +59,12 @@ public RemoteInstanceTO(String instanceName, String instancePath, String vcenter this.datacenterName = datacenterName; } + public RemoteInstanceTO(String instanceName, String instancePath, String vcenterHost, String vcenterUsername, String vcenterPassword, String datacenterName, String clusterName, String hostName) { + this(instanceName, instancePath, vcenterHost, vcenterUsername, vcenterPassword, datacenterName); + this.clusterName = clusterName; + this.hostName = hostName; + } + public Hypervisor.HypervisorType getHypervisorType() { return this.hypervisorType; } @@ -82,4 +92,12 @@ public String getVcenterHost() { public String getDatacenterName() { return datacenterName; } + + public String getClusterName() { + return clusterName; + } + + public String getHostName() { + return hostName; + } } diff --git a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineMetadataTO.java b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineMetadataTO.java new file mode 100644 index 000000000000..5b22afdedd53 --- /dev/null +++ b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineMetadataTO.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 com.cloud.agent.api.to; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class VirtualMachineMetadataTO { + // VM details + private final String name; + private final String internalName; + private final String displayName; + private final String instanceUuid; + private final Integer cpuCores; + private final Integer memory; + private final Long created; + private final Long started; + + // Owner details + private final String ownerDomainUuid; + private final String ownerDomainName; + private final String ownerAccountUuid; + private final String ownerAccountName; + private final String ownerProjectUuid; + private final String ownerProjectName; + + // Host and service offering + private final String serviceOfferingName; + private final List serviceOfferingHostTags; + + // zone, pod, and cluster details + private final String zoneName; + private final String zoneUuid; + private final String podName; + private final String podUuid; + private final String clusterName; + private final String clusterUuid; + + // resource tags + private final Map resourceTags; + + public VirtualMachineMetadataTO( + String name, String internalName, String displayName, String instanceUuid, Integer cpuCores, Integer memory, Long created, Long started, + String ownerDomainUuid, String ownerDomainName, String ownerAccountUuid, String ownerAccountName, String ownerProjectUuid, String ownerProjectName, + String serviceOfferingName, List serviceOfferingHostTags, + String zoneName, String zoneUuid, String podName, String podUuid, String clusterName, String clusterUuid, Map resourceTags) { + /* + * Something failed in the metadata shall not be a fatal error, the VM can still be started + * Thus, the unknown fields just get an explicit "unknown" value so it can be fixed in case + * there are bugs on some execution paths. + * */ + + this.name = (name != null) ? name : "unknown"; + this.internalName = (internalName != null) ? internalName : "unknown"; + this.displayName = (displayName != null) ? displayName : "unknown"; + this.instanceUuid = (instanceUuid != null) ? instanceUuid : "unknown"; + this.cpuCores = (cpuCores != null) ? cpuCores : -1; + this.memory = (memory != null) ? memory : -1; + this.created = (created != null) ? created : 0; + this.started = (started != null) ? started : 0; + this.ownerDomainUuid = (ownerDomainUuid != null) ? ownerDomainUuid : "unknown"; + this.ownerDomainName = (ownerDomainName != null) ? ownerDomainName : "unknown"; + this.ownerAccountUuid = (ownerAccountUuid != null) ? ownerAccountUuid : "unknown"; + this.ownerAccountName = (ownerAccountName != null) ? ownerAccountName : "unknown"; + this.ownerProjectUuid = (ownerProjectUuid != null) ? ownerProjectUuid : "unknown"; + this.ownerProjectName = (ownerProjectName != null) ? ownerProjectName : "unknown"; + this.serviceOfferingName = (serviceOfferingName != null) ? serviceOfferingName : "unknown"; + this.serviceOfferingHostTags = (serviceOfferingHostTags != null) ? serviceOfferingHostTags : new ArrayList<>(); + this.zoneName = (zoneName != null) ? zoneName : "unknown"; + this.zoneUuid = (zoneUuid != null) ? zoneUuid : "unknown"; + this.podName = (podName != null) ? podName : "unknown"; + this.podUuid = (podUuid != null) ? podUuid : "unknown"; + this.clusterName = (clusterName != null) ? clusterName : "unknown"; + this.clusterUuid = (clusterUuid != null) ? clusterUuid : "unknown"; + + this.resourceTags = (resourceTags != null) ? resourceTags : new HashMap<>(); + } + + public String getName() { + return name; + } + + public String getInternalName() { + return internalName; + } + + public String getDisplayName() { + return displayName; + } + + public String getInstanceUuid() { + return instanceUuid; + } + + public Integer getCpuCores() { + return cpuCores; + } + + public Integer getMemory() { + return memory; + } + + public Long getCreated() { return created; } + + public Long getStarted() { + return started; + } + + public String getOwnerDomainUuid() { + return ownerDomainUuid; + } + + public String getOwnerDomainName() { + return ownerDomainName; + } + + public String getOwnerAccountUuid() { + return ownerAccountUuid; + } + + public String getOwnerAccountName() { + return ownerAccountName; + } + + public String getOwnerProjectUuid() { + return ownerProjectUuid; + } + + public String getOwnerProjectName() { + return ownerProjectName; + } + + public String getserviceOfferingName() { + return serviceOfferingName; + } + + public List getserviceOfferingHostTags() { + return serviceOfferingHostTags; + } + + public String getZoneName() { + return zoneName; + } + + public String getZoneUuid() { + return zoneUuid; + } + + public String getPodName() { + return podName; + } + + public String getPodUuid() { + return podUuid; + } + + public String getClusterName() { + return clusterName; + } + + public String getClusterUuid() { + return clusterUuid; + } + + public Map getResourceTags() { return resourceTags; } +} 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 cffb98740805..9af6c731fd24 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 @@ -51,6 +51,7 @@ public class VirtualMachineTO { private long minRam; private long maxRam; + private long requestedRam; private String hostName; private String arch; private String os; @@ -89,6 +90,7 @@ public class VirtualMachineTO { private DeployAsIsInfoTO deployAsIsInfo; private String metadataManufacturer; private String metadataProductName; + private VirtualMachineMetadataTO metadata; 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) { @@ -206,15 +208,20 @@ public long getMinRam() { return minRam; } - public void setRam(long minRam, long maxRam) { + public void setRam(long minRam, long maxRam, long requestedRam) { this.minRam = minRam; this.maxRam = maxRam; + this.requestedRam = requestedRam; } public long getMaxRam() { return maxRam; } + public long getRequestedRam() { + return requestedRam; + } + public String getHostName() { return hostName; } @@ -494,6 +501,14 @@ public void setMetadataProductName(String metadataProductName) { this.metadataProductName = metadataProductName; } + public VirtualMachineMetadataTO getMetadata() { + return metadata; + } + + public void setMetadata(VirtualMachineMetadataTO metadata) { + this.metadata = metadata; + } + @Override public String toString() { return String.format("VM {id: \"%s\", name: \"%s\", uuid: \"%s\", type: \"%s\"}", id, name, uuid, type); diff --git a/api/src/main/java/com/cloud/agent/manager/allocator/HostAllocator.java b/api/src/main/java/com/cloud/agent/manager/allocator/HostAllocator.java index 604720aaa290..5d028d31d5b6 100644 --- a/api/src/main/java/com/cloud/agent/manager/allocator/HostAllocator.java +++ b/api/src/main/java/com/cloud/agent/manager/allocator/HostAllocator.java @@ -22,19 +22,11 @@ import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.host.Host; import com.cloud.host.Host.Type; -import com.cloud.offering.ServiceOffering; import com.cloud.utils.component.Adapter; -import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; public interface HostAllocator extends Adapter { - /** - * @param UserVm vm - * @param ServiceOffering offering - **/ - boolean isVirtualMachineUpgradable(final VirtualMachine vm, final ServiceOffering offering); - /** * Determines which physical hosts are suitable to * allocate the guest virtual machines on @@ -49,31 +41,6 @@ public interface HostAllocator extends Adapter { public List allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, int returnUpTo); - /** - * Determines which physical hosts are suitable to allocate the guest - * virtual machines on - * - * Allocators must set any other hosts not considered for allocation in the - * ExcludeList avoid. Thus the avoid set and the list of hosts suitable, - * together must cover the entire host set in the cluster. - * - * @param VirtualMachineProfile - * vmProfile - * @param DeploymentPlan - * plan - * @param GuestType - * type - * @param ExcludeList - * avoid - * @param int returnUpTo (use -1 to return all possible hosts) - * @param boolean considerReservedCapacity (default should be true, set to - * false if host capacity calculation should not look at reserved - * capacity) - * @return List List of hosts that are suitable for VM allocation - **/ - - public List allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, int returnUpTo, boolean considerReservedCapacity); - /** * Determines which physical hosts are suitable to allocate the guest * virtual machines on diff --git a/api/src/main/java/com/cloud/api/commands/ListRecurringSnapshotScheduleCmd.java b/api/src/main/java/com/cloud/api/commands/ListRecurringSnapshotScheduleCmd.java index d34c09c94fde..d8aa13710e23 100644 --- a/api/src/main/java/com/cloud/api/commands/ListRecurringSnapshotScheduleCmd.java +++ b/api/src/main/java/com/cloud/api/commands/ListRecurringSnapshotScheduleCmd.java @@ -35,10 +35,10 @@ public class ListRecurringSnapshotScheduleCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.SNAPSHOT_POLICY_ID, type = CommandType.LONG, description = "lists recurring snapshots by snapshot policy ID") + @Parameter(name = ApiConstants.SNAPSHOT_POLICY_ID, type = CommandType.LONG, description = "Lists recurring Snapshots by Snapshot policy ID") private Long snapshotPolicyId; - @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.LONG, required = true, description = "list recurring snapshots by volume ID") + @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.LONG, required = true, description = "List recurring Snapshots by volume ID") private Long volumeId; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/com/cloud/configuration/ConfigurationService.java b/api/src/main/java/com/cloud/configuration/ConfigurationService.java index 32e31519ea78..729f72b23ca2 100644 --- a/api/src/main/java/com/cloud/configuration/ConfigurationService.java +++ b/api/src/main/java/com/cloud/configuration/ConfigurationService.java @@ -24,15 +24,18 @@ 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.CloneNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.network.CreateGuestNetworkIpv6PrefixCmd; import org.apache.cloudstack.api.command.admin.network.CreateManagementNetworkIpRangeCmd; -import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.network.DeleteGuestNetworkIpv6PrefixCmd; import org.apache.cloudstack.api.command.admin.network.DeleteManagementNetworkIpRangeCmd; import org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.network.ListGuestNetworkIpv6PrefixesCmd; +import org.apache.cloudstack.api.command.admin.network.NetworkOfferingBaseCmd; import org.apache.cloudstack.api.command.admin.network.UpdateNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.network.UpdatePodManagementNetworkIpRangeCmd; +import org.apache.cloudstack.api.command.admin.offering.CloneDiskOfferingCmd; +import org.apache.cloudstack.api.command.admin.offering.CloneServiceOfferingCmd; import org.apache.cloudstack.api.command.admin.offering.CreateDiskOfferingCmd; import org.apache.cloudstack.api.command.admin.offering.CreateServiceOfferingCmd; import org.apache.cloudstack.api.command.admin.offering.DeleteDiskOfferingCmd; @@ -105,39 +108,52 @@ public interface ConfigurationService { */ ServiceOffering createServiceOffering(CreateServiceOfferingCmd cmd); + /** + * Clones a service offering with optional parameter overrides + * + * @param cmd + * the command object that specifies the source offering ID and optional parameter overrides + * @return the newly created service offering cloned from source, null otherwise + */ + ServiceOffering cloneServiceOffering(CloneServiceOfferingCmd cmd); + + /** + * Clones a disk offering with optional parameter overrides + * + * @param cmd + * the command object that specifies the source offering ID and optional parameter overrides + * @return the newly created disk offering cloned from source, null otherwise + */ + DiskOffering cloneDiskOffering(CloneDiskOfferingCmd cmd); + + /** + * Clones a network offering with optional parameter overrides + * + * @param cmd + * the command object that specifies the source offering ID and optional parameter overrides + * @return the newly created network offering cloned from source, null otherwise + */ + NetworkOffering cloneNetworkOffering(CloneNetworkOfferingCmd cmd); + /** * Updates a service offering * - * @param serviceOfferingId - * @param userId - * @param name - * @param displayText - * @param offerHA - * @param useVirtualNetwork - * @param tags * @return updated service offering */ ServiceOffering updateServiceOffering(UpdateServiceOfferingCmd cmd); /** * Deletes a service offering - * - * @param userId - * @param serviceOfferingId */ boolean deleteServiceOffering(DeleteServiceOfferingCmd cmd); /** * Retrieve ID of domains for a service offering - * - * @param serviceOfferingId */ List getServiceOfferingDomains(Long serviceOfferingId); /** * Retrieve ID of domains for a service offering - * - * @param serviceOfferingId */ List getServiceOfferingZones(Long serviceOfferingId); @@ -147,7 +163,6 @@ public interface ConfigurationService { * @param cmd * - the command specifying diskOfferingId, name, description, tags * @return updated disk offering - * @throws */ DiskOffering updateDiskOffering(UpdateDiskOfferingCmd cmd); @@ -157,34 +172,22 @@ public interface ConfigurationService { * @param cmd * - the command specifying disk offering id * @return true or false - * @throws */ boolean deleteDiskOffering(DeleteDiskOfferingCmd cmd); /** * Creates a new disk offering - * - * @param domainId - * @param name - * @param description - * @param numGibibytes - * @param mirrored - * @param size * @return ID */ DiskOffering createDiskOffering(CreateDiskOfferingCmd cmd); /** * Retrieve ID of domains for a disk offering - * - * @param diskOfferingId */ List getDiskOfferingDomains(Long diskOfferingId); /** * Retrieve ID of domains for a disk offering - * - * @param diskOfferingId */ List getDiskOfferingZones(Long diskOfferingId); @@ -207,8 +210,6 @@ public interface ConfigurationService { * 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, List storageAccessGroups); @@ -228,8 +229,7 @@ public interface ConfigurationService { /** * Updates a mutually exclusive IP range in the pod. * @param cmd - The command specifying pod ID, current Start IP, current End IP, new Start IP, new End IP. - * @throws com.cloud.exception.ConcurrentOperationException - * @return Success + * @throws com.cloud.exception.ConcurrentOperationException when this pod is already being accessed */ void updatePodIpRange(UpdatePodManagementNetworkIpRangeCmd cmd) throws ConcurrentOperationException; @@ -250,9 +250,6 @@ public interface ConfigurationService { /** * Edits a pod in the database. Will not allow you to edit pods that are being used anywhere in the system. - * - * @param UpdatePodCmd - * api command */ Pod editPod(UpdatePodCmd cmd); @@ -262,17 +259,12 @@ public interface ConfigurationService { * @param cmd * - the command containing podId * @return true or false - * @throws , */ boolean deletePod(DeletePodCmd cmd); /** * Creates a new zone - * - * @param cmd * @return the zone if successful, null otherwise - * @throws - * @throws */ DataCenter createZone(CreateZoneCmd cmd); @@ -295,22 +287,7 @@ public interface ConfigurationService { * Adds a VLAN to the database, along with an IP address range. Can add three types of VLANs: (1) zone-wide VLANs on * the * virtual public network (2) pod-wide direct attached VLANs (3) account-specific direct attached VLANs - * - * @param userId - * @param vlanType - * - either "DomR" (VLAN for a virtual public network) or "DirectAttached" (VLAN for IPs that will be - * directly - * attached to UserVMs) - * @param zoneId - * @param accountId - * @param podId - * @param add - * @param vlanId - * @param gateway - * @param startIP - * @param endIP * @throws ResourceAllocationException TODO - * @throws * @return The new Vlan object */ Vlan createVlanAndPublicIpRange(CreateVlanIpRangeCmd cmd) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, @@ -325,9 +302,6 @@ Vlan updateVlanAndPublicIpRange(UpdateVlanIpRangeCmd cmd) throws ConcurrentOpera /** * Marks the account with the default zone-id. * - * @param accountName - * @param domainId - * @param defaultZoneId * @return The new account object */ Account markDefaultZone(String accountName, long domainId, long defaultZoneId); @@ -338,7 +312,7 @@ Vlan updateVlanAndPublicIpRange(UpdateVlanIpRangeCmd cmd) throws ConcurrentOpera boolean releasePublicIpRange(ReleasePublicIpRangeCmd cmd); - NetworkOffering createNetworkOffering(CreateNetworkOfferingCmd cmd); + NetworkOffering createNetworkOffering(NetworkOfferingBaseCmd cmd); NetworkOffering updateNetworkOffering(UpdateNetworkOfferingCmd cmd); @@ -349,14 +323,12 @@ Vlan updateVlanAndPublicIpRange(UpdateVlanIpRangeCmd cmd) throws ConcurrentOpera /** * Retrieve ID of domains for a network offering * - * @param networkOfferingId */ List getNetworkOfferingDomains(Long networkOfferingId); /** * Retrieve ID of domains for a network offering * - * @param networkOfferingId */ List getNetworkOfferingZones(Long networkOfferingId); diff --git a/api/src/main/java/com/cloud/cpu/CPU.java b/api/src/main/java/com/cloud/cpu/CPU.java index 3016e542db65..11b38b73da53 100644 --- a/api/src/main/java/com/cloud/cpu/CPU.java +++ b/api/src/main/java/com/cloud/cpu/CPU.java @@ -22,7 +22,8 @@ public class CPU { public enum CPUArch { x86("i686", 32), amd64("x86_64", 64), - arm64("aarch64", 64); + arm64("aarch64", 64), + s390x("s390x", 64); private final String type; private final int bits; diff --git a/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java b/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java index d127e4bdd660..9471c3d5c84c 100644 --- a/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java +++ b/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java @@ -62,11 +62,11 @@ public interface DeploymentClusterPlanner extends DeploymentPlanner { "vm.allocation.algorithm", "Advanced", "random", - "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'.", + "Order in which hosts within a cluster will be considered for VM allocation. The value can be 'random', 'firstfit', 'userdispersing', or 'firstfitleastconsumed'.", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.Select, - "random,firstfit,userdispersing,userconcentratedpod_random,userconcentratedpod_firstfit,firstfitleastconsumed"); + "random,firstfit,userdispersing,firstfitleastconsumed"); /** * This is called to determine list of possible clusters where a virtual diff --git a/api/src/main/java/com/cloud/deploy/DeploymentPlanner.java b/api/src/main/java/com/cloud/deploy/DeploymentPlanner.java index 354f9cfaac53..22d796d4a775 100644 --- a/api/src/main/java/com/cloud/deploy/DeploymentPlanner.java +++ b/api/src/main/java/com/cloud/deploy/DeploymentPlanner.java @@ -70,7 +70,7 @@ public interface DeploymentPlanner extends Adapter { boolean canHandle(VirtualMachineProfile vm, DeploymentPlan plan, ExcludeList avoid); public enum AllocationAlgorithm { - random, firstfit, userdispersing, userconcentratedpod_random, userconcentratedpod_firstfit; + random, firstfit, userdispersing, firstfitleastconsumed; } public enum PlannerResourceUsage { diff --git a/api/src/main/java/com/cloud/event/EventTypes.java b/api/src/main/java/com/cloud/event/EventTypes.java index be21f13267b1..f7d13343d469 100644 --- a/api/src/main/java/com/cloud/event/EventTypes.java +++ b/api/src/main/java/com/cloud/event/EventTypes.java @@ -27,14 +27,20 @@ import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.PodResponse; import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.backup.BackupRepositoryService; import org.apache.cloudstack.config.Configuration; import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; +import org.apache.cloudstack.dns.DnsRecord; +import org.apache.cloudstack.dns.DnsServer; +import org.apache.cloudstack.dns.DnsZone; 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.kms.HSMProfile; +import org.apache.cloudstack.kms.KMSKey; import org.apache.cloudstack.network.BgpPeer; import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap; import org.apache.cloudstack.quota.QuotaTariff; @@ -42,7 +48,7 @@ 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; +import org.apache.cloudstack.schedule.ResourceSchedule; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenterGuestIpv6Prefix; @@ -124,17 +130,18 @@ public class EventTypes { public static final String EVENT_VM_UNMANAGE = "VM.UNMANAGE"; public static final String EVENT_VM_RECOVER = "VM.RECOVER"; - // VM Schedule - public static final String EVENT_VM_SCHEDULE_CREATE = "VM.SCHEDULE.CREATE"; - public static final String EVENT_VM_SCHEDULE_UPDATE = "VM.SCHEDULE.UPDATE"; - public static final String EVENT_VM_SCHEDULE_DELETE = "VM.SCHEDULE.DELETE"; - + // VM Schedule action-execution events (fired when a scheduled action runs). public static final String EVENT_VM_SCHEDULE_START = "VM.SCHEDULE.START"; public static final String EVENT_VM_SCHEDULE_STOP = "VM.SCHEDULE.STOP"; public static final String EVENT_VM_SCHEDULE_REBOOT = "VM.SCHEDULE.REBOOT"; public static final String EVENT_VM_SCHEDULE_FORCE_STOP = "VM.SCHEDULE.FORCE.STOP"; public static final String EVENT_VM_SCHEDULE_FORCE_REBOOT = "VM.SCHEDULE.FORCE.REBOOT"; + // Generic Resource Schedule CRUD events (apply to all resource types). + public static final String EVENT_SCHEDULE_CREATE = "SCHEDULE.CREATE"; + public static final String EVENT_SCHEDULE_UPDATE = "SCHEDULE.UPDATE"; + public static final String EVENT_SCHEDULE_DELETE = "SCHEDULE.DELETE"; + // Domain Router public static final String EVENT_ROUTER_CREATE = "ROUTER.CREATE"; public static final String EVENT_ROUTER_DESTROY = "ROUTER.DESTROY"; @@ -270,6 +277,20 @@ public class EventTypes { public static final String EVENT_CA_CERTIFICATE_REVOKE = "CA.CERTIFICATE.REVOKE"; public static final String EVENT_CA_CERTIFICATE_PROVISION = "CA.CERTIFICATE.PROVISION"; + // KMS (Key Management Service) events + public static final String EVENT_KMS_KEY_WRAP = "KMS.KEY.WRAP"; + public static final String EVENT_KMS_KEY_UNWRAP = "KMS.KEY.UNWRAP"; + public static final String EVENT_KMS_KEY_CREATE = "KMS.KEY.CREATE"; + public static final String EVENT_KMS_KEY_UPDATE = "KMS.KEY.UPDATE"; + public static final String EVENT_KMS_KEY_ROTATE = "KMS.KEY.ROTATE"; + public static final String EVENT_KMS_KEY_DELETE = "KMS.KEY.DELETE"; + public static final String EVENT_VOLUME_MIGRATE_TO_KMS = "VOLUME.MIGRATE.TO.KMS"; + + // HSM Profile events + public static final String EVENT_HSM_PROFILE_CREATE = "HSM.PROFILE.CREATE"; + public static final String EVENT_HSM_PROFILE_UPDATE = "HSM.PROFILE.UPDATE"; + public static final String EVENT_HSM_PROFILE_DELETE = "HSM.PROFILE.DELETE"; + // Account events public static final String EVENT_ACCOUNT_ENABLE = "ACCOUNT.ENABLE"; public static final String EVENT_ACCOUNT_DISABLE = "ACCOUNT.DISABLE"; @@ -297,8 +318,9 @@ public class EventTypes { 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 + //user API and secret keys public static final String EVENT_REGISTER_FOR_SECRET_API_KEY = "REGISTER.USER.KEY"; + public static final String EVENT_DELETE_SECRET_API_KEY = "DELETE.USER.KEY"; public static final String API_KEY_ACCESS_UPDATE = "API.KEY.ACCESS.UPDATE"; // Template Events @@ -373,11 +395,13 @@ public class EventTypes { // Service Offerings public static final String EVENT_SERVICE_OFFERING_CREATE = "SERVICE.OFFERING.CREATE"; + public static final String EVENT_SERVICE_OFFERING_CLONE = "SERVICE.OFFERING.CLONE"; public static final String EVENT_SERVICE_OFFERING_EDIT = "SERVICE.OFFERING.EDIT"; public static final String EVENT_SERVICE_OFFERING_DELETE = "SERVICE.OFFERING.DELETE"; // Disk Offerings public static final String EVENT_DISK_OFFERING_CREATE = "DISK.OFFERING.CREATE"; + public static final String EVENT_DISK_OFFERING_CLONE = "DISK.OFFERING.CLONE"; public static final String EVENT_DISK_OFFERING_EDIT = "DISK.OFFERING.EDIT"; public static final String EVENT_DISK_OFFERING_DELETE = "DISK.OFFERING.DELETE"; @@ -398,6 +422,7 @@ public class EventTypes { // Network offerings public static final String EVENT_NETWORK_OFFERING_CREATE = "NETWORK.OFFERING.CREATE"; + public static final String EVENT_NETWORK_OFFERING_CLONE = "NETWORK.OFFERING.CLONE"; public static final String EVENT_NETWORK_OFFERING_ASSIGN = "NETWORK.OFFERING.ASSIGN"; public static final String EVENT_NETWORK_OFFERING_EDIT = "NETWORK.OFFERING.EDIT"; public static final String EVENT_NETWORK_OFFERING_REMOVE = "NETWORK.OFFERING.REMOVE"; @@ -502,6 +527,7 @@ public class EventTypes { public static final String EVENT_S2S_VPN_CUSTOMER_GATEWAY_CREATE = "VPN.S2S.CUSTOMER.GATEWAY.CREATE"; public static final String EVENT_S2S_VPN_CUSTOMER_GATEWAY_DELETE = "VPN.S2S.CUSTOMER.GATEWAY.DELETE"; public static final String EVENT_S2S_VPN_CUSTOMER_GATEWAY_UPDATE = "VPN.S2S.CUSTOMER.GATEWAY.UPDATE"; + public static final String EVENT_S2S_VPN_GATEWAY_OBSOLETE_PARAMS = "VPN.S2S.GATEWAY.OBSOLETE.PARAMS"; public static final String EVENT_S2S_VPN_CONNECTION_CREATE = "VPN.S2S.CONNECTION.CREATE"; public static final String EVENT_S2S_VPN_CONNECTION_DELETE = "VPN.S2S.CONNECTION.DELETE"; public static final String EVENT_S2S_VPN_CONNECTION_RESET = "VPN.S2S.CONNECTION.RESET"; @@ -581,6 +607,7 @@ public class EventTypes { // Network ACL public static final String EVENT_NETWORK_ACL_CREATE = "NETWORK.ACL.CREATE"; + public static final String EVENT_NETWORK_ACL_IMPORT = "NETWORK.ACL.IMPORT"; public static final String EVENT_NETWORK_ACL_DELETE = "NETWORK.ACL.DELETE"; public static final String EVENT_NETWORK_ACL_REPLACE = "NETWORK.ACL.REPLACE"; public static final String EVENT_NETWORK_ACL_UPDATE = "NETWORK.ACL.UPDATE"; @@ -595,6 +622,7 @@ public class EventTypes { // VPC offerings public static final String EVENT_VPC_OFFERING_CREATE = "VPC.OFFERING.CREATE"; + public static final String EVENT_VPC_OFFERING_CLONE = "VPC.OFFERING.CLONE"; public static final String EVENT_VPC_OFFERING_UPDATE = "VPC.OFFERING.UPDATE"; public static final String EVENT_VPC_OFFERING_DELETE = "VPC.OFFERING.DELETE"; @@ -627,6 +655,7 @@ public class EventTypes { // Backup and Recovery events public static final String EVENT_VM_BACKUP_IMPORT_OFFERING = "BACKUP.IMPORT.OFFERING"; + public static final String EVENT_VM_BACKUP_OFFERING_CLONE = "BACKUP.OFFERING.CLONE"; public static final String EVENT_VM_BACKUP_OFFERING_ASSIGN = "BACKUP.OFFERING.ASSIGN"; public static final String EVENT_VM_BACKUP_OFFERING_REMOVE = "BACKUP.OFFERING.REMOVE"; public static final String EVENT_VM_BACKUP_CREATE = "BACKUP.CREATE"; @@ -639,6 +668,7 @@ public class EventTypes { 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"; + public static final String EVENT_SCREENSHOT_DOWNLOAD = "BACKUP.VALIDATION.SCREENSHOT.DOWNLOAD"; // external network device events public static final String EVENT_EXTERNAL_NVP_CONTROLLER_ADD = "PHYSICAL.NVPCONTROLLER.ADD"; @@ -667,6 +697,7 @@ public class EventTypes { public static final String EVENT_AUTOSCALEVMGROUP_DISABLE = "AUTOSCALEVMGROUP.DISABLE"; public static final String EVENT_AUTOSCALEVMGROUP_SCALEDOWN = "AUTOSCALEVMGROUP.SCALEDOWN"; public static final String EVENT_AUTOSCALEVMGROUP_SCALEUP = "AUTOSCALEVMGROUP.SCALEUP"; + public static final String EVENT_AUTOSCALEVMGROUP_SCHEDULE_UPDATE = "AUTOSCALEVMGROUP.SCHEDULE.UPDATE"; public static final String EVENT_BAREMETAL_DHCP_SERVER_ADD = "PHYSICAL.DHCP.ADD"; public static final String EVENT_BAREMETAL_DHCP_SERVER_DELETE = "PHYSICAL.DHCP.DELETE"; @@ -845,6 +876,7 @@ public class EventTypes { 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_RESOURCE_UPDATE = "EXTENSION.RESOURCE.UPDATE"; 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"; @@ -852,6 +884,21 @@ public class EventTypes { // Custom Action public static final String EVENT_CUSTOM_ACTION = "CUSTOM.ACTION"; + // Backup Repository + public static final String EVENT_BACKUP_REPOSITORY_ADD = "BACKUP.REPOSITORY.ADD"; + public static final String EVENT_BACKUP_REPOSITORY_UPDATE = "BACKUP.REPOSITORY.UPDATE"; + + // DNS Framework Events + public static final String EVENT_DNS_SERVER_ADD = "DNS.SERVER.ADD"; + public static final String EVENT_DNS_SERVER_UPDATE = "DNS.SERVER.UPDATE"; + public static final String EVENT_DNS_SERVER_DELETE = "DNS.SERVER.DELETE"; + public static final String EVENT_DNS_ZONE_CREATE = "DNS.ZONE.CREATE"; + public static final String EVENT_DNS_ZONE_UPDATE = "DNS.ZONE.UPDATE"; + public static final String EVENT_DNS_ZONE_DELETE = "DNS.ZONE.DELETE"; + public static final String EVENT_DNS_RECORD_CREATE = "DNS.RECORD.CREATE"; + public static final String EVENT_DNS_RECORD_DELETE = "DNS.RECORD.DELETE"; + public static final String EVENT_DNS_NAME_COLLISION = "DNS.NAME.COLLISION"; + static { // TODO: need a way to force author adding event types to declare the entity details as well, with out braking @@ -875,15 +922,18 @@ public class EventTypes { entityEventDetails.put(EVENT_VM_IMPORT, VirtualMachine.class); entityEventDetails.put(EVENT_VM_UNMANAGE, VirtualMachine.class); - // VMSchedule - entityEventDetails.put(EVENT_VM_SCHEDULE_CREATE, VMSchedule.class); - entityEventDetails.put(EVENT_VM_SCHEDULE_DELETE, VMSchedule.class); - entityEventDetails.put(EVENT_VM_SCHEDULE_UPDATE, VMSchedule.class); - entityEventDetails.put(EVENT_VM_SCHEDULE_START, VMSchedule.class); - entityEventDetails.put(EVENT_VM_SCHEDULE_STOP, VMSchedule.class); - entityEventDetails.put(EVENT_VM_SCHEDULE_REBOOT, VMSchedule.class); - entityEventDetails.put(EVENT_VM_SCHEDULE_FORCE_STOP, VMSchedule.class); - entityEventDetails.put(EVENT_VM_SCHEDULE_FORCE_REBOOT, VMSchedule.class); + // VMSchedule action-execution events + entityEventDetails.put(EVENT_VM_SCHEDULE_START, ResourceSchedule.class); + entityEventDetails.put(EVENT_VM_SCHEDULE_STOP, ResourceSchedule.class); + entityEventDetails.put(EVENT_VM_SCHEDULE_REBOOT, ResourceSchedule.class); + entityEventDetails.put(EVENT_VM_SCHEDULE_FORCE_STOP, ResourceSchedule.class); + entityEventDetails.put(EVENT_VM_SCHEDULE_FORCE_REBOOT, ResourceSchedule.class); + entityEventDetails.put(EVENT_AUTOSCALEVMGROUP_SCHEDULE_UPDATE, ResourceSchedule.class); + + // Generic Resource Schedule + entityEventDetails.put(EVENT_SCHEDULE_CREATE, ResourceSchedule.class); + entityEventDetails.put(EVENT_SCHEDULE_UPDATE, ResourceSchedule.class); + entityEventDetails.put(EVENT_SCHEDULE_DELETE, ResourceSchedule.class); entityEventDetails.put(EVENT_ROUTER_CREATE, VirtualRouter.class); entityEventDetails.put(EVENT_ROUTER_DESTROY, VirtualRouter.class); @@ -1002,6 +1052,20 @@ public class EventTypes { entityEventDetails.put(EVENT_VOLUME_RECOVER, Volume.class); entityEventDetails.put(EVENT_VOLUME_CHANGE_DISK_OFFERING, Volume.class); + // KMS Key Events + entityEventDetails.put(EVENT_KMS_KEY_CREATE, KMSKey.class); + entityEventDetails.put(EVENT_KMS_KEY_UPDATE, KMSKey.class); + entityEventDetails.put(EVENT_KMS_KEY_UNWRAP, KMSKey.class); + entityEventDetails.put(EVENT_KMS_KEY_WRAP, KMSKey.class); + entityEventDetails.put(EVENT_KMS_KEY_DELETE, KMSKey.class); + entityEventDetails.put(EVENT_KMS_KEY_ROTATE, KMSKey.class); + entityEventDetails.put(EVENT_VOLUME_MIGRATE_TO_KMS, KMSKey.class); + + // HSM Profile Events + entityEventDetails.put(EVENT_HSM_PROFILE_CREATE, HSMProfile.class); + entityEventDetails.put(EVENT_HSM_PROFILE_UPDATE, HSMProfile.class); + entityEventDetails.put(EVENT_HSM_PROFILE_DELETE, HSMProfile.class); + // Domains entityEventDetails.put(EVENT_DOMAIN_CREATE, Domain.class); entityEventDetails.put(EVENT_DOMAIN_DELETE, Domain.class); @@ -1038,11 +1102,13 @@ public class EventTypes { // Service Offerings entityEventDetails.put(EVENT_SERVICE_OFFERING_CREATE, ServiceOffering.class); + entityEventDetails.put(EVENT_SERVICE_OFFERING_CLONE, ServiceOffering.class); entityEventDetails.put(EVENT_SERVICE_OFFERING_EDIT, ServiceOffering.class); entityEventDetails.put(EVENT_SERVICE_OFFERING_DELETE, ServiceOffering.class); // Disk Offerings entityEventDetails.put(EVENT_DISK_OFFERING_CREATE, DiskOffering.class); + entityEventDetails.put(EVENT_DISK_OFFERING_CLONE, DiskOffering.class); entityEventDetails.put(EVENT_DISK_OFFERING_EDIT, DiskOffering.class); entityEventDetails.put(EVENT_DISK_OFFERING_DELETE, DiskOffering.class); @@ -1063,6 +1129,7 @@ public class EventTypes { // Network offerings entityEventDetails.put(EVENT_NETWORK_OFFERING_CREATE, NetworkOffering.class); + entityEventDetails.put(EVENT_NETWORK_OFFERING_CLONE, NetworkOffering.class); entityEventDetails.put(EVENT_NETWORK_OFFERING_ASSIGN, NetworkOffering.class); entityEventDetails.put(EVENT_NETWORK_OFFERING_EDIT, NetworkOffering.class); entityEventDetails.put(EVENT_NETWORK_OFFERING_REMOVE, NetworkOffering.class); @@ -1146,6 +1213,7 @@ public class EventTypes { entityEventDetails.put(EVENT_S2S_VPN_CUSTOMER_GATEWAY_CREATE, Site2SiteCustomerGateway.class); entityEventDetails.put(EVENT_S2S_VPN_CUSTOMER_GATEWAY_DELETE, Site2SiteCustomerGateway.class); entityEventDetails.put(EVENT_S2S_VPN_CUSTOMER_GATEWAY_UPDATE, Site2SiteCustomerGateway.class); + entityEventDetails.put(EVENT_S2S_VPN_GATEWAY_OBSOLETE_PARAMS, Site2SiteCustomerGateway.class); entityEventDetails.put(EVENT_S2S_VPN_CONNECTION_CREATE, Site2SiteVpnConnection.class); entityEventDetails.put(EVENT_S2S_VPN_CONNECTION_DELETE, Site2SiteVpnConnection.class); entityEventDetails.put(EVENT_S2S_VPN_CONNECTION_RESET, Site2SiteVpnConnection.class); @@ -1385,6 +1453,21 @@ public class EventTypes { 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); + + // Backup Repository + entityEventDetails.put(EVENT_BACKUP_REPOSITORY_ADD, BackupRepositoryService.class); + entityEventDetails.put(EVENT_BACKUP_REPOSITORY_UPDATE, BackupRepositoryService.class); + + // DNS Framework Events + entityEventDetails.put(EVENT_DNS_SERVER_ADD, DnsServer.class); + entityEventDetails.put(EVENT_DNS_SERVER_UPDATE, DnsServer.class); + entityEventDetails.put(EVENT_DNS_SERVER_DELETE, DnsServer.class); + entityEventDetails.put(EVENT_DNS_ZONE_CREATE, DnsZone.class); + entityEventDetails.put(EVENT_DNS_ZONE_UPDATE, DnsZone.class); + entityEventDetails.put(EVENT_DNS_ZONE_DELETE, DnsZone.class); + entityEventDetails.put(EVENT_DNS_RECORD_CREATE, DnsRecord.class); + entityEventDetails.put(EVENT_DNS_RECORD_DELETE, DnsRecord.class); + } public static boolean isNetworkEvent(String eventType) { diff --git a/api/src/main/java/com/cloud/ha/Investigator.java b/api/src/main/java/com/cloud/ha/Investigator.java index 88d802a1ce44..00371d395f5a 100644 --- a/api/src/main/java/com/cloud/ha/Investigator.java +++ b/api/src/main/java/com/cloud/ha/Investigator.java @@ -26,17 +26,19 @@ public interface Investigator extends Adapter { * Returns if the vm is still alive. * * @param vm to work on. + * @return true if vm is alive, otherwise false */ - public boolean isVmAlive(VirtualMachine vm, Host host) throws UnknownVM; + boolean isVmAlive(VirtualMachine vm, Host host) throws UnknownVM; - public Status isAgentAlive(Host agent); + /** + * Returns the agent status of the host. + * + * @param host + * @return status of the host agent + */ + Status getHostAgentStatus(Host host); class UnknownVM extends Exception { - - /** - * - */ private static final long serialVersionUID = 1L; - }; } diff --git a/api/src/main/java/com/cloud/host/Host.java b/api/src/main/java/com/cloud/host/Host.java index 07a0dfce0419..c110e4ca94e1 100644 --- a/api/src/main/java/com/cloud/host/Host.java +++ b/api/src/main/java/com/cloud/host/Host.java @@ -57,8 +57,17 @@ public static String[] toStrings(Host.Type... types) { String HOST_UEFI_ENABLE = "host.uefi.enable"; String HOST_VOLUME_ENCRYPTION = "host.volume.encryption"; String HOST_INSTANCE_CONVERSION = "host.instance.conversion"; + String HOST_VDDK_SUPPORT = "host.vddk.support"; + String HOST_VDDK_LIB_DIR = "vddk.lib.dir"; + String HOST_VDDK_VERSION = "host.vddk.version"; String HOST_OVFTOOL_VERSION = "host.ovftool.version"; String HOST_VIRTV2V_VERSION = "host.virtv2v.version"; + String HOST_SSH_PORT = "host.ssh.port"; + String HOST_CDROM_MAX_COUNT = "host.cdrom.max.count"; + String GUEST_OS_CATEGORY_ID = "guest.os.category.id"; + String GUEST_OS_RULE = "guest.os.rule"; + + int DEFAULT_SSH_PORT = 22; /** * @return name of the machine. diff --git a/api/src/main/java/com/cloud/host/HostStats.java b/api/src/main/java/com/cloud/host/HostStats.java index d14794401fa3..0e72b5f2d9d0 100644 --- a/api/src/main/java/com/cloud/host/HostStats.java +++ b/api/src/main/java/com/cloud/host/HostStats.java @@ -36,5 +36,4 @@ public interface HostStats { public HostStats getHostStats(); public double getLoadAverage(); - // public double getXapiMemoryUsageKBs(); } diff --git a/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java b/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java index 0c821b4e36c0..67db19b7cc54 100644 --- a/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java +++ b/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java @@ -20,6 +20,7 @@ import java.util.Map; import org.apache.cloudstack.backup.Backup; +import org.apache.cloudstack.backup.BackupProvider; import org.apache.cloudstack.framework.config.ConfigKey; import com.cloud.agent.api.Command; @@ -94,10 +95,10 @@ public interface HypervisorGuru extends Adapter { Map getClusterSettings(long vmId); VirtualMachine importVirtualMachineFromBackup(long zoneId, long domainId, long accountId, long userId, - String vmInternalName, Backup backup) throws Exception; + String vmInternalName, Backup backup, BackupProvider backupProvider) throws Exception; boolean attachRestoredVolumeToVirtualMachine(long zoneId, String location, Backup.VolumeInfo volumeInfo, - VirtualMachine vm, long poolId, Backup backup) throws Exception; + VirtualMachine vm, long poolId, Backup backup, BackupProvider backupProvider) throws Exception; /** * Will generate commands to migrate a vm to a pool. For now this will only work for stopped VMs on Vmware. * diff --git a/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java index 571d993c7a10..80f6a6045c72 100644 --- a/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java +++ b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java @@ -88,6 +88,8 @@ enum State { s_fsm.addTransition(State.Stopping, Event.OperationFailed, State.Alert); s_fsm.addTransition(State.Stopped, Event.StartRequested, State.Starting); + s_fsm.addTransition(State.Stopped, Event.OperationSucceeded, State.Stopped); + s_fsm.addTransition(State.Running, Event.OperationSucceeded, State.Running); s_fsm.addTransition(State.Running, Event.FaultsDetected, State.Alert); @@ -96,7 +98,7 @@ enum State { 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.Scaling, Event.OperationFailed, State.Running); s_fsm.addTransition(State.ScalingStoppedCluster, Event.OperationSucceeded, State.Stopped); s_fsm.addTransition(State.ScalingStoppedCluster, Event.OperationFailed, State.Alert); @@ -170,4 +172,5 @@ enum State { Long getEtcdNodeCount(); Long getCniConfigId(); String getCniConfigDetails(); + boolean isCsiEnabled(); } 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 37b8907b454a..5a6eaa3f7b9a 100644 --- a/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesServiceHelper.java +++ b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesServiceHelper.java @@ -18,6 +18,7 @@ import org.apache.cloudstack.acl.ControlledEntity; +import java.util.List; import java.util.Map; import com.cloud.user.Account; @@ -33,8 +34,10 @@ enum KubernetesClusterNodeType { ControlledEntity findByUuid(String uuid); ControlledEntity findByVmId(long vmId); void checkVmCanBeDestroyed(UserVm userVm); + void checkVmAffinityGroupsCanBeUpdated(UserVm userVm); boolean isValidNodeType(String nodeType); Map getServiceOfferingNodeTypeMap(Map> serviceOfferingNodeTypeMap); Map getTemplateNodeTypeMap(Map> templateNodeTypeMap); + Map> getAffinityGroupNodeTypeMap(Map> affinityGroupNodeTypeMap); void cleanupForAccount(Account account); } diff --git a/api/src/main/java/com/cloud/network/Ipv6Service.java b/api/src/main/java/com/cloud/network/Ipv6Service.java index 4ef5f98c38d8..e6c3b9250a7a 100644 --- a/api/src/main/java/com/cloud/network/Ipv6Service.java +++ b/api/src/main/java/com/cloud/network/Ipv6Service.java @@ -45,7 +45,7 @@ public interface Ipv6Service extends PluggableService, Configurable { static final ConfigKey Ipv6OfferingCreationEnabled = new ConfigKey("Advanced", Boolean.class, "ipv6.offering.enabled", "false", - "Indicates whether creation of IPv6 network/VPC offering is enabled or not.", + "Indicates whether creation of IPv6 Network/VPC offering is enabled or not.", true); static final ConfigKey Ipv6PrefixSubnetCleanupInterval = new ConfigKey("Advanced", Integer.class, diff --git a/api/src/main/java/com/cloud/network/Network.java b/api/src/main/java/com/cloud/network/Network.java index dc94932e31f5..2f0bcdd5ef9a 100644 --- a/api/src/main/java/com/cloud/network/Network.java +++ b/api/src/main/java/com/cloud/network/Network.java @@ -116,6 +116,7 @@ class Service { public static final Service NetworkACL = new Service("NetworkACL", Capability.SupportedProtocols); public static final Service Connectivity = new Service("Connectivity", Capability.DistributedRouter, Capability.RegionLevelVpc, Capability.StretchedL2Subnet, Capability.NoVlan, Capability.PublicAccess); + public static final Service CustomAction = new Service("CustomAction"); private final String name; private final Capability[] caps; @@ -207,6 +208,7 @@ public static class Provider { public static final Provider Nsx = new Provider("Nsx", false); public static final Provider Netris = new Provider("Netris", false); + public static final Provider NetworkExtension = new Provider("NetworkExtension", false, true); private final String name; private final boolean isExternal; @@ -250,11 +252,47 @@ public static Provider getProvider(String providerName) { return null; } + /** Private constructor for transient (non-registered) providers. */ + private Provider(String name) { + this.name = name; + this.isExternal = false; + this.needCleanupOnShutdown = true; + // intentionally NOT added to supportedProviders + } + + /** + * Creates a transient (non-registered) {@link Provider} with the given name. + * + *

The new instance is not added to {@code supportedProviders}, so it + * will never be returned by {@link #getProvider(String)} and will not pollute the + * global provider registry. Use this for dynamic / extension-backed providers + * whose names are only known at runtime (e.g. NetworkOrchestrator extensions).

+ * + * @param name the provider name (typically the extension name) + * @return a transient {@link Provider} instance with the given name + */ + public static Provider createTransientProvider(String name) { + return new Provider(name); + } + @Override public String toString() { return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) .append("name", name) .toString(); } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Provider)) return false; + Provider provider = (Provider) obj; + return this.name.equals(provider.name); + } + + @Override + public int hashCode() { + return name.hashCode(); + } } public static class Capability { @@ -325,9 +363,9 @@ enum Event { public enum State { - Allocated("Indicates the network configuration is in allocated but not setup"), Setup("Indicates the network configuration is setup"), Implementing( - "Indicates the network configuration is being implemented"), Implemented("Indicates the network configuration is in use"), Shutdown( - "Indicates the network configuration is being destroyed"), Destroy("Indicates that the network is destroyed"); + Allocated("Indicates the Network configuration is in allocated but not setup"), Setup("Indicates the Network configuration is setup"), Implementing( + "Indicates the Network configuration is being implemented"), Implemented("Indicates the Network configuration is in use"), Shutdown( + "Indicates the Network configuration is being destroyed"), Destroy("Indicates that the Network is destroyed"); protected static final StateMachine2 s_fsm = new StateMachine2(); @@ -510,4 +548,6 @@ public void setIp6Address(String ip6Address) { Integer getPrivateMtu(); Integer getNetworkCidrSize(); + + boolean getKeepMacAddressOnPublicNic(); } diff --git a/api/src/main/java/com/cloud/network/NetworkModel.java b/api/src/main/java/com/cloud/network/NetworkModel.java index eb496ac4e0b9..7e1a07ebeb69 100644 --- a/api/src/main/java/com/cloud/network/NetworkModel.java +++ b/api/src/main/java/com/cloud/network/NetworkModel.java @@ -125,6 +125,10 @@ public interface NetworkModel { */ String getNextAvailableMacAddressInNetwork(long networkConfigurationId) throws InsufficientAddressCapacityException; + String getUniqueMacAddress(long macAddress, long networkId, long datacenterId) throws InsufficientAddressCapacityException; + + boolean isMACUnique(String mac, long networkId); + PublicIpAddress getPublicIpAddress(long ipAddressId); List listPodVlans(long podId); @@ -183,6 +187,8 @@ public interface NetworkModel { boolean canElementEnableIndividualServices(Provider provider); + boolean canElementEnableIndividualServicesByName(String providerName); + boolean areServicesSupportedInNetwork(long networkId, Service... services); boolean isNetworkSystem(Network network); @@ -233,6 +239,18 @@ public interface NetworkModel { String getDefaultGuestTrafficLabel(long dcId, HypervisorType vmware); + /** + * Resolves a provider name to a {@link Provider} instance. + * For known static providers, delegates to {@link Provider#getProvider(String)}. + * For dynamically-registered NetworkOrchestrator extension providers whose names + * are not in the static registry, returns a transient {@link Provider} with the + * given name so callers can still dispatch correctly. + * + * @param providerName the provider name from {@code ntwk_service_map} or similar + * @return a {@link Provider} instance, or {@code null} if not resolvable + */ + Provider resolveProvider(String providerName); + /** * @param providerName * @return @@ -364,4 +382,8 @@ List generateVmData(String userData, String userDataDetails, String se boolean checkSecurityGroupSupportForNetwork(Account account, DataCenter zone, List networkIds, List securityGroupsIds); + + default long getMacIdentifier(Long dataCenterId) { + return 0; + } } diff --git a/api/src/main/java/com/cloud/network/NetworkProfile.java b/api/src/main/java/com/cloud/network/NetworkProfile.java index 2e8efb489308..d690344a0e38 100644 --- a/api/src/main/java/com/cloud/network/NetworkProfile.java +++ b/api/src/main/java/com/cloud/network/NetworkProfile.java @@ -385,6 +385,11 @@ public Integer getNetworkCidrSize() { return networkCidrSize; } + @Override + public boolean getKeepMacAddressOnPublicNic() { + return true; + } + @Override public String toString() { return String.format("NetworkProfile %s", diff --git a/api/src/main/java/com/cloud/network/NetworkRuleApplier.java b/api/src/main/java/com/cloud/network/NetworkRuleApplier.java index b9942e71eb26..69b712bc6ca2 100644 --- a/api/src/main/java/com/cloud/network/NetworkRuleApplier.java +++ b/api/src/main/java/com/cloud/network/NetworkRuleApplier.java @@ -21,8 +21,13 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.rules.FirewallRule; +import com.cloud.network.vpc.Vpc; public interface NetworkRuleApplier { - public boolean applyRules(Network network, FirewallRule.Purpose purpose, List rules) throws ResourceUnavailableException; + default boolean applyRules(Network network, FirewallRule.Purpose purpose, List rules) throws ResourceUnavailableException { + return applyRules(network, null, purpose, rules); + } + + boolean applyRules(Network network, Vpc vpc, FirewallRule.Purpose purpose, List rules) throws ResourceUnavailableException; } diff --git a/api/src/main/java/com/cloud/network/NetworkService.java b/api/src/main/java/com/cloud/network/NetworkService.java index 196e1f9aab86..c32bb711c0f2 100644 --- a/api/src/main/java/com/cloud/network/NetworkService.java +++ b/api/src/main/java/com/cloud/network/NetworkService.java @@ -81,7 +81,7 @@ public interface NetworkService { true, ConfigKey.Scope.Zone); public static final ConfigKey AllowUsersToSpecifyVRMtu = new ConfigKey<>("Advanced", Boolean.class, - "allow.end.users.to.specify.vr.mtu", "false", "Allow end users to specify VR MTU", + "allow.end.users.to.specify.vr.mtu", "false", "Allow end Users to specify VR MTU", true, ConfigKey.Scope.Zone); List getIsolatedNetworksOwnedByAccountInZone(long zoneId, Account owner); @@ -108,6 +108,10 @@ Network createGuestNetwork(long networkOfferingId, String name, String displayTe PhysicalNetwork physicalNetwork, long zoneId, ControlledEntity.ACLType aclType) throws InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException; + Network createGuestNetwork(long networkOfferingId, String name, String displayText, Account owner, + PhysicalNetwork physicalNetwork, long zoneId, ControlledEntity.ACLType aclType, Pair vrIfaceMTUs) throws + InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException; + Pair, Integer> searchForNetworks(ListNetworksCmd cmd); boolean deleteNetwork(long networkId, boolean forced); @@ -228,7 +232,7 @@ Network createPrivateNetwork(String networkName, String displayText, long physic /** * Requests an IP address for the guest NIC */ - NicSecondaryIp allocateSecondaryGuestIP(long nicId, IpAddresses requestedIpPair) throws InsufficientAddressCapacityException; + NicSecondaryIp allocateSecondaryGuestIP(long nicId, IpAddresses requestedIpPair, String description) throws InsufficientAddressCapacityException; boolean releaseSecondaryIpFromNic(long ipAddressId); @@ -275,4 +279,6 @@ Network createPrivateNetwork(String networkName, String displayText, long physic IpAddresses getIpAddressesFromIps(String ipAddress, String ip6Address, String macAddress); String getNicVlanValueForExternalVm(NicTO nic); + + Long getPreferredNetworkIdForPublicIpRuleAssignment(IpAddress ip, Long networkId); } diff --git a/api/src/main/java/com/cloud/network/Networks.java b/api/src/main/java/com/cloud/network/Networks.java index 9f06a0441118..61a1c820723f 100644 --- a/api/src/main/java/com/cloud/network/Networks.java +++ b/api/src/main/java/com/cloud/network/Networks.java @@ -78,10 +78,14 @@ public URI toUri(T value) { } @Override public String getValueFrom(URI uri) { - return uri.getAuthority(); + return uri == null ? null : uri.getAuthority(); } }, - Vswitch("vs", String.class), LinkLocal(null, null), Vnet("vnet", Long.class), Storage("storage", Integer.class), Lswitch("lswitch", String.class) { + Vswitch("vs", String.class), + LinkLocal(null, null), + Vnet("vnet", Long.class), + Storage("storage", Integer.class), + Lswitch("lswitch", String.class) { @Override public URI toUri(T value) { try { @@ -96,10 +100,11 @@ public URI toUri(T value) { */ @Override public String getValueFrom(URI uri) { - return uri.getSchemeSpecificPart(); + return uri == null ? null : uri.getSchemeSpecificPart(); } }, - Mido("mido", String.class), Pvlan("pvlan", String.class), + Mido("mido", String.class), + Pvlan("pvlan", String.class), Vxlan("vxlan", Long.class) { @Override public URI toUri(T value) { @@ -177,7 +182,7 @@ public URI toUri(T value) { * @return the scheme as BroadcastDomainType */ public static BroadcastDomainType getSchemeValue(URI uri) { - return toEnumValue(uri.getScheme()); + return toEnumValue(uri == null ? null : uri.getScheme()); } /** @@ -191,7 +196,7 @@ public static BroadcastDomainType getTypeOf(String str) throws URISyntaxExceptio if (com.cloud.dc.Vlan.UNTAGGED.equalsIgnoreCase(str)) { return Native; } - return getSchemeValue(new URI(str)); + return getSchemeValue(str == null ? null : new URI(str)); } /** @@ -220,7 +225,7 @@ public static BroadcastDomainType toEnumValue(String scheme) { * @return the host part as String */ public String getValueFrom(URI uri) { - return uri.getHost(); + return uri == null ? null : uri.getHost(); } /** @@ -243,7 +248,7 @@ public static String getValue(URI uri) { * @throws URISyntaxException the string is not even an uri */ public static String getValue(String uriString) throws URISyntaxException { - return getValue(new URI(uriString)); + return getValue(uriString == null ? null : new URI(uriString)); } /** diff --git a/api/src/main/java/com/cloud/network/PhysicalNetworkTrafficType.java b/api/src/main/java/com/cloud/network/PhysicalNetworkTrafficType.java index 9676badb4e90..d3804cd29daf 100644 --- a/api/src/main/java/com/cloud/network/PhysicalNetworkTrafficType.java +++ b/api/src/main/java/com/cloud/network/PhysicalNetworkTrafficType.java @@ -41,4 +41,6 @@ public interface PhysicalNetworkTrafficType extends InternalIdentity, Identity { String getHypervNetworkLabel(); String getOvm3NetworkLabel(); + + String getVlan(); } diff --git a/api/src/main/java/com/cloud/network/RouterHealthCheckResult.java b/api/src/main/java/com/cloud/network/RouterHealthCheckResult.java index eb65ae9088ec..22a46ce9ecdf 100644 --- a/api/src/main/java/com/cloud/network/RouterHealthCheckResult.java +++ b/api/src/main/java/com/cloud/network/RouterHealthCheckResult.java @@ -26,7 +26,7 @@ public interface RouterHealthCheckResult { String getCheckType(); - boolean getCheckResult(); + VirtualNetworkApplianceService.RouterHealthStatus getCheckResult(); Date getLastUpdateTime(); diff --git a/api/src/main/java/com/cloud/network/VirtualNetworkApplianceService.java b/api/src/main/java/com/cloud/network/VirtualNetworkApplianceService.java index cb92739d2837..a60f1d49336a 100644 --- a/api/src/main/java/com/cloud/network/VirtualNetworkApplianceService.java +++ b/api/src/main/java/com/cloud/network/VirtualNetworkApplianceService.java @@ -87,4 +87,8 @@ void startRouterForHA(VirtualMachine vm, Map performRouterHealthChecks(long routerId); void collectNetworkStatistics(T router, Nic nic); + + enum RouterHealthStatus{ + SUCCESS, FAILED, WARNING, UNKNOWN; + } } diff --git a/api/src/main/java/com/cloud/network/as/AutoScaleService.java b/api/src/main/java/com/cloud/network/as/AutoScaleService.java index ceca4de68428..4aef10f8de9e 100644 --- a/api/src/main/java/com/cloud/network/as/AutoScaleService.java +++ b/api/src/main/java/com/cloud/network/as/AutoScaleService.java @@ -70,6 +70,8 @@ public interface AutoScaleService { Counter createCounter(CreateCounterCmd cmd); + Counter getCounter(long counterId); + boolean deleteCounter(long counterId) throws ResourceInUseException; List listCounters(ListCountersCmd cmd); diff --git a/api/src/main/java/com/cloud/network/as/AutoScaleVmGroup.java b/api/src/main/java/com/cloud/network/as/AutoScaleVmGroup.java index c265c1f36a0a..c05b3bda3db5 100644 --- a/api/src/main/java/com/cloud/network/as/AutoScaleVmGroup.java +++ b/api/src/main/java/com/cloud/network/as/AutoScaleVmGroup.java @@ -43,7 +43,7 @@ public static State fromValue(String state) { } else if (state.equalsIgnoreCase("scaling")) { return SCALING; } else { - throw new IllegalArgumentException("Unexpected AutoScale VM group state : " + state); + throw new IllegalArgumentException("Unexpected AutoScale Instance group state : " + state); } } } diff --git a/api/src/main/java/com/cloud/network/element/DnsServiceProvider.java b/api/src/main/java/com/cloud/network/element/DnsServiceProvider.java index 7abce537221c..2942e76965a7 100644 --- a/api/src/main/java/com/cloud/network/element/DnsServiceProvider.java +++ b/api/src/main/java/com/cloud/network/element/DnsServiceProvider.java @@ -33,4 +33,6 @@ boolean configDnsSupportForSubnet(Network network, NicProfile nic, VirtualMachin throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException; boolean removeDnsSupportForSubnet(Network network) throws ResourceUnavailableException; + + default boolean removeDnsEntry(Network network, NicProfile nic, VirtualMachineProfile vmProfile) throws ResourceUnavailableException { return true; } } diff --git a/api/src/main/java/com/cloud/network/element/FirewallServiceProvider.java b/api/src/main/java/com/cloud/network/element/FirewallServiceProvider.java index c091142d9353..6b0f932e8c22 100644 --- a/api/src/main/java/com/cloud/network/element/FirewallServiceProvider.java +++ b/api/src/main/java/com/cloud/network/element/FirewallServiceProvider.java @@ -21,14 +21,20 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.Network; import com.cloud.network.rules.FirewallRule; +import com.cloud.network.vpc.Vpc; public interface FirewallServiceProvider extends NetworkElement { /** - * Apply rules - * @param network - * @param rules - * @return - * @throws ResourceUnavailableException + * Apply firewall rules in a network context. */ - boolean applyFWRules(Network network, List rules) throws ResourceUnavailableException; + default boolean applyFWRules(Network network, List rules) throws ResourceUnavailableException { + return false; + } + + /** + * Apply firewall rules in a VPC context. + */ + default boolean applyFWRulesInVPC(Vpc vpc, List rules) throws ResourceUnavailableException { + return false; + } } 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 cb0fc2fca981..67be7b9ba2e2 100644 --- a/api/src/main/java/com/cloud/network/element/NetworkElement.java +++ b/api/src/main/java/com/cloud/network/element/NetworkElement.java @@ -146,4 +146,8 @@ boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, Reser * @return true/false */ boolean verifyServicesCombination(Set services); + + default boolean rollingRestartSupported() { + 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 3fc6028b9771..b7fe3b26761c 100644 --- a/api/src/main/java/com/cloud/network/lb/LoadBalancingRulesService.java +++ b/api/src/main/java/com/cloud/network/lb/LoadBalancingRulesService.java @@ -41,13 +41,23 @@ public interface LoadBalancingRulesService { /** * Create a load balancer rule from the given ipAddress/port to the given private port + * @param xId an existing UUID for this rule (for instance a device generated one) + * @param name + * @param description + * @param srcPortStart + * @param srcPortEnd + * @param defPortStart + * @param defPortEnd + * @param ipAddrId + * @param protocol + * @param algorithm + * @param networkId + * @param lbOwnerId * @param openFirewall - * TODO - * @param forDisplay TODO - * @param cmd - * the command specifying the ip address, public port, protocol, private port, and algorithm - * + * @param lbProtocol + * @param forDisplay * @return the newly created LoadBalancerVO if successful, null otherwise + * @throws NetworkRuleConflictException * @throws InsufficientAddressCapacityException */ LoadBalancer createPublicLoadBalancerRule(String xId, String name, String description, int srcPortStart, int srcPortEnd, int defPortStart, int defPortEnd, @@ -98,7 +108,7 @@ LoadBalancer createPublicLoadBalancerRule(String xId, String name, String descri /** * Assign a virtual machine or list of virtual machines, or Map of to a load balancer. */ - boolean assignToLoadBalancer(long lbRuleId, List vmIds, Map> vmIdIpMap, boolean isAutoScaleVM); + boolean assignToLoadBalancer(long lbRuleId, List vmIds, Map> vmIdIpMap, Map vmIdNetworkMap, boolean isAutoScaleVM); boolean assignSSLCertToLoadBalancerRule(Long lbRuleId, String certName, String publicCert, String privateKey); diff --git a/api/src/main/java/com/cloud/network/rules/FirewallRule.java b/api/src/main/java/com/cloud/network/rules/FirewallRule.java index 369c6aa57eb8..38ba009163ce 100644 --- a/api/src/main/java/com/cloud/network/rules/FirewallRule.java +++ b/api/src/main/java/com/cloud/network/rules/FirewallRule.java @@ -69,7 +69,9 @@ enum TrafficType { State getState(); - long getNetworkId(); + Long getNetworkId(); + + Long getVpcId(); Long getSourceIpAddressId(); diff --git a/api/src/main/java/com/cloud/network/rules/LbStickinessMethod.java b/api/src/main/java/com/cloud/network/rules/LbStickinessMethod.java index 56a0622a52ba..5143611ee828 100644 --- a/api/src/main/java/com/cloud/network/rules/LbStickinessMethod.java +++ b/api/src/main/java/com/cloud/network/rules/LbStickinessMethod.java @@ -108,8 +108,7 @@ public LbStickinessMethod(StickinessMethodType methodType, String description) { } public void addParam(String name, Boolean required, String description, Boolean isFlag) { - /* FIXME : UI is breaking if the capability string length is larger , temporarily description is commented out */ - // LbStickinessMethodParam param = new LbStickinessMethodParam(name, required, description); + /* is this still a valid comment: FIXME : UI is breaking if the capability string length is larger , temporarily description is commented out */ LbStickinessMethodParam param = new LbStickinessMethodParam(name, required, " ", isFlag); _paramList.add(param); return; @@ -133,7 +132,6 @@ public String getDescription() { public void setDescription(String description) { /* FIXME : UI is breaking if the capability string length is larger , temporarily description is commented out */ - //this.description = description; this._description = " "; } } diff --git a/api/src/main/java/com/cloud/network/vpc/NetworkACLService.java b/api/src/main/java/com/cloud/network/vpc/NetworkACLService.java index 40aee1f08f1d..84e48d5d5b8a 100644 --- a/api/src/main/java/com/cloud/network/vpc/NetworkACLService.java +++ b/api/src/main/java/com/cloud/network/vpc/NetworkACLService.java @@ -19,6 +19,7 @@ import java.util.List; import org.apache.cloudstack.api.command.user.network.CreateNetworkACLCmd; +import org.apache.cloudstack.api.command.user.network.ImportNetworkACLCmd; import org.apache.cloudstack.api.command.user.network.ListNetworkACLListsCmd; import org.apache.cloudstack.api.command.user.network.ListNetworkACLsCmd; import org.apache.cloudstack.api.command.user.network.MoveNetworkAclItemCmd; @@ -98,4 +99,6 @@ public interface NetworkACLService { NetworkACLItem moveNetworkAclRuleToNewPosition(MoveNetworkAclItemCmd moveNetworkAclItemCmd); NetworkACLItem moveRuleToTheTopInACLList(NetworkACLItem ruleBeingMoved); + + List importNetworkACLRules(ImportNetworkACLCmd cmd) throws ResourceUnavailableException; } 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 b94089d2d433..a0686e2bf7d0 100644 --- a/api/src/main/java/com/cloud/network/vpc/Vpc.java +++ b/api/src/main/java/com/cloud/network/vpc/Vpc.java @@ -107,4 +107,6 @@ public enum State { String getIp6Dns2(); boolean useRouterIpAsResolver(); + + boolean getKeepMacAddressOnPublicNic(); } 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 17f49bb36521..f84602232159 100644 --- a/api/src/main/java/com/cloud/network/vpc/VpcOffering.java +++ b/api/src/main/java/com/cloud/network/vpc/VpcOffering.java @@ -84,4 +84,6 @@ public enum State { NetworkOffering.RoutingMode getRoutingMode(); Boolean isSpecifyAsNumber(); + + boolean isConserveMode(); } 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 97b95339ecf3..891cfb02d9df 100644 --- a/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java +++ b/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Map; +import org.apache.cloudstack.api.command.admin.vpc.CloneVPCOfferingCmd; import org.apache.cloudstack.api.command.admin.vpc.CreateVPCOfferingCmd; import org.apache.cloudstack.api.command.admin.vpc.UpdateVPCOfferingCmd; import org.apache.cloudstack.api.command.user.vpc.ListVPCOfferingsCmd; @@ -34,12 +35,14 @@ public interface VpcProvisioningService { VpcOffering createVpcOffering(CreateVPCOfferingCmd cmd); + VpcOffering cloneVPCOffering(CloneVPCOfferingCmd cmd); + VpcOffering createVpcOffering(String name, String displayText, List supportedServices, Map> serviceProviders, Map serviceCapabilitystList, NetUtils.InternetProtocol internetProtocol, Long serviceOfferingId, String externalProvider, NetworkOffering.NetworkMode networkMode, List domainIds, List zoneIds, VpcOffering.State state, - NetworkOffering.RoutingMode routingMode, boolean specifyAsNumber); + NetworkOffering.RoutingMode routingMode, boolean specifyAsNumber, boolean conserveMode); Pair,Integer> listVpcOfferings(ListVPCOfferingsCmd cmd); 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 c1546609d2b7..3d0ba43263f5 100644 --- a/api/src/main/java/com/cloud/network/vpc/VpcService.java +++ b/api/src/main/java/com/cloud/network/vpc/VpcService.java @@ -58,7 +58,7 @@ public interface VpcService { */ 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, Boolean useVrIpResolver) throws ResourceAllocationException; + Long asNumber, List bgpPeerIds, Boolean useVrIpResolver, boolean keepMacAddressOnPublicNic) throws ResourceAllocationException; /** * Persists VPC record in the database @@ -104,7 +104,7 @@ Vpc createVpc(long zoneId, long vpcOffId, long vpcOwnerId, String vpcName, Strin * @throws ResourceUnavailableException if during restart some resources may not be available * @throws InsufficientCapacityException if for instance no address space, compute or storage is sufficiently available */ - Vpc updateVpc(long vpcId, String vpcName, String displayText, String customId, Boolean displayVpc, Integer mtu, String sourceNatIp) throws ResourceUnavailableException, InsufficientCapacityException; + Vpc updateVpc(long vpcId, String vpcName, String displayText, String customId, Boolean displayVpc, Integer mtu, String sourceNatIp, Boolean keepMacAddressOnPublicNic) throws ResourceUnavailableException, InsufficientCapacityException; /** * Lists VPC(s) based on the parameters passed to the API call diff --git a/api/src/main/java/com/cloud/offering/DiskOffering.java b/api/src/main/java/com/cloud/offering/DiskOffering.java index e1c41f77cbf5..d74f5703cc99 100644 --- a/api/src/main/java/com/cloud/offering/DiskOffering.java +++ b/api/src/main/java/com/cloud/offering/DiskOffering.java @@ -37,7 +37,7 @@ enum State { State getState(); enum DiskCacheMode { - NONE("none"), WRITEBACK("writeback"), WRITETHROUGH("writethrough"); + NONE("none"), WRITEBACK("writeback"), WRITETHROUGH("writethrough"), HYPERVISOR_DEFAULT("hypervisor_default"); private final String _diskCacheMode; @@ -69,6 +69,8 @@ public String toString() { boolean isCustomized(); + boolean isShared(); + void setDiskSize(long diskSize); long getDiskSize(); @@ -99,7 +101,6 @@ public String toString() { Long getBytesReadRateMaxLength(); - void setBytesWriteRate(Long bytesWriteRate); Long getBytesWriteRate(); @@ -112,7 +113,6 @@ public String toString() { Long getBytesWriteRateMaxLength(); - void setIopsReadRate(Long iopsReadRate); Long getIopsReadRate(); @@ -133,7 +133,6 @@ public String toString() { Long getIopsWriteRateMax(); - void setIopsWriteRateMaxLength(Long iopsWriteRateMaxLength); Long getIopsWriteRateMaxLength(); diff --git a/api/src/main/java/com/cloud/offering/DiskOfferingInfo.java b/api/src/main/java/com/cloud/offering/DiskOfferingInfo.java index 12dcf423e34f..197565a1fccb 100644 --- a/api/src/main/java/com/cloud/offering/DiskOfferingInfo.java +++ b/api/src/main/java/com/cloud/offering/DiskOfferingInfo.java @@ -23,6 +23,7 @@ public class DiskOfferingInfo { private Long _size; private Long _minIops; private Long _maxIops; + private Long _kmsKeyId; public DiskOfferingInfo() { } @@ -38,6 +39,14 @@ public DiskOfferingInfo(DiskOffering diskOffering, Long size, Long minIops, Long _maxIops = maxIops; } + public DiskOfferingInfo(DiskOffering diskOffering, Long size, Long minIops, Long maxIops, Long kmsKeyId) { + _diskOffering = diskOffering; + _size = size; + _minIops = minIops; + _maxIops = maxIops; + _kmsKeyId = kmsKeyId; + } + public void setDiskOffering(DiskOffering diskOffering) { _diskOffering = diskOffering; } @@ -69,4 +78,12 @@ public void setMaxIops(Long maxIops) { public Long getMaxIops() { return _maxIops; } + + public void setKmsKeyId(Long kmsKeyId) { + _kmsKeyId = kmsKeyId; + } + + public Long getKmsKeyId() { + return _kmsKeyId; + } } diff --git a/api/src/main/java/com/cloud/projects/ProjectService.java b/api/src/main/java/com/cloud/projects/ProjectService.java index 5080cb5a7812..d11e9ae0446d 100644 --- a/api/src/main/java/com/cloud/projects/ProjectService.java +++ b/api/src/main/java/com/cloud/projects/ProjectService.java @@ -82,7 +82,7 @@ public interface ProjectService { Project updateProject(long id, String name, String displayText, String newOwnerName, Long userId, Role newRole) throws ResourceAllocationException; - boolean addAccountToProject(long projectId, String accountName, String email, Long projectRoleId, Role projectRoleType); + boolean addAccountToProject(long projectId, String accountName, String email, Long projectRoleId, Role projectRoleType) throws ResourceAllocationException; boolean deleteAccountFromProject(long projectId, String accountName); @@ -100,6 +100,6 @@ public interface ProjectService { Project findByProjectAccountIdIncludingRemoved(long projectAccountId); - boolean addUserToProject(Long projectId, String username, String email, Long projectRoleId, Role projectRole); + boolean addUserToProject(Long projectId, String username, String email, Long projectRoleId, Role projectRole) throws ResourceAllocationException; } diff --git a/api/src/main/java/com/cloud/server/ManagementService.java b/api/src/main/java/com/cloud/server/ManagementService.java index f7b2456ce5cc..bcca229c06bc 100644 --- a/api/src/main/java/com/cloud/server/ManagementService.java +++ b/api/src/main/java/com/cloud/server/ManagementService.java @@ -20,7 +20,6 @@ import java.util.List; import java.util.Map; -import com.cloud.user.UserData; 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; @@ -77,6 +76,8 @@ import com.cloud.capacity.Capacity; import com.cloud.dc.Pod; import com.cloud.dc.Vlan; +import com.cloud.deploy.DeploymentPlan; +import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.ManagementServerException; import com.cloud.exception.ResourceUnavailableException; @@ -91,11 +92,13 @@ import com.cloud.storage.GuestOsCategory; import com.cloud.storage.StoragePool; import com.cloud.user.SSHKeyPair; +import com.cloud.user.UserData; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; import com.cloud.vm.InstanceGroup; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; +import com.cloud.vm.VirtualMachineProfile; /** * Hopefully this is temporary. @@ -469,6 +472,19 @@ public interface ManagementService { Ternary, Integer>, List, Map> listHostsForMigrationOfVM(VirtualMachine vm, Long startIndex, Long pageSize, String keyword, List vmList); + /** + * Apply affinity group constraints and other exclusion rules for VM migration. + * This is a helper method that can be used independently for per-iteration affinity checks in DRS. + * + * @param vm The virtual machine to migrate + * @param vmProfile The VM profile + * @param plan The deployment plan + * @param vmList List of VMs with current/simulated placements for affinity processing + * @return ExcludeList containing hosts to avoid + */ + ExcludeList applyAffinityConstraints(VirtualMachine vm, VirtualMachineProfile vmProfile, + DeploymentPlan plan, List vmList); + /** * List storage pools for live migrating of a volume. The API returns list of all pools in the cluster to which the * volume can be migrated. Current pool is not included in the list. In case of vSphere datastore cluster storage pools, diff --git a/api/src/main/java/com/cloud/server/ResourceManagerUtil.java b/api/src/main/java/com/cloud/server/ResourceManagerUtil.java index 9a3b51a70d56..f5081cbe307b 100644 --- a/api/src/main/java/com/cloud/server/ResourceManagerUtil.java +++ b/api/src/main/java/com/cloud/server/ResourceManagerUtil.java @@ -18,6 +18,7 @@ public interface ResourceManagerUtil { long getResourceId(String resourceId, ResourceTag.ResourceObjectType resourceType); + long getResourceId(String resourceId, ResourceTag.ResourceObjectType resourceType, boolean checkAccess); String getUuid(String resourceId, ResourceTag.ResourceObjectType resourceType); ResourceTag.ResourceObjectType getResourceType(String resourceTypeStr); void checkResourceAccessible(Long accountId, Long domainId, String exceptionMessage); diff --git a/api/src/main/java/com/cloud/server/ResourceTag.java b/api/src/main/java/com/cloud/server/ResourceTag.java index b3026deceff8..32305753f1ae 100644 --- a/api/src/main/java/com/cloud/server/ResourceTag.java +++ b/api/src/main/java/com/cloud/server/ResourceTag.java @@ -16,14 +16,14 @@ // under the License. package com.cloud.server; -import org.apache.cloudstack.acl.ControlledEntity; -import org.apache.cloudstack.api.Identity; -import org.apache.cloudstack.api.InternalIdentity; - import java.util.HashMap; import java.util.Locale; import java.util.Map; +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + public interface ResourceTag extends ControlledEntity, Identity, InternalIdentity { // FIXME - extract enum to another interface as its used both by resourceTags and resourceMetaData code @@ -70,7 +70,7 @@ public enum ResourceObjectType { GuestOs(false, true), NetworkOffering(false, true), VpcOffering(true, false), - Domain(false, false, true), + Domain(true, false, true), ObjectStore(false, false, true); diff --git a/api/src/main/java/com/cloud/storage/Storage.java b/api/src/main/java/com/cloud/storage/Storage.java index 1ad3731b9eaa..3511b4e88cb9 100644 --- a/api/src/main/java/com/cloud/storage/Storage.java +++ b/api/src/main/java/com/cloud/storage/Storage.java @@ -35,7 +35,8 @@ public static enum ImageFormat { VDI(true, true, false, "vdi"), TAR(false, false, false, "tar"), ZIP(false, false, false, "zip"), - DIR(false, false, false, "dir"); + DIR(false, false, false, "dir"), + PNG(false, false, false, "png"); private final boolean supportThinProvisioning; private final boolean supportSparse; @@ -128,7 +129,7 @@ public static enum FileSystem { public static enum TemplateType { ROUTING, // Router template SYSTEM, /* routing, system vm template */ - BUILTIN, /* buildin template */ + BUILTIN, /* builtin template */ PERHOST, /* every host has this template, don't need to install it in secondary storage */ USER, /* User supplied template/iso */ VNF, /* VNFs (virtual network functions) template */ @@ -170,6 +171,7 @@ public static enum StoragePoolType { ISO(false, false, EncryptionSupport.Unsupported), // for iso image LVM(false, false, EncryptionSupport.Unsupported), // XenServer local LVM SR CLVM(true, false, EncryptionSupport.Unsupported), + CLVM_NG(true, false, EncryptionSupport.Hypervisor), RBD(true, true, EncryptionSupport.Unsupported), // http://libvirt.org/storage.html#StorageBackendRBD SharedMountPoint(true, true, EncryptionSupport.Hypervisor), VMFS(true, true, EncryptionSupport.Unsupported), // VMware VMFS storage diff --git a/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java b/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java index db702a61f2bc..7d5b2d7c57d7 100644 --- a/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java +++ b/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java @@ -23,9 +23,10 @@ public interface VMTemplateStorageResourceAssoc extends InternalIdentity { public static enum Status { - UNKNOWN, DOWNLOAD_ERROR, NOT_DOWNLOADED, DOWNLOAD_IN_PROGRESS, DOWNLOADED, ABANDONED, UPLOADED, NOT_UPLOADED, UPLOAD_ERROR, UPLOAD_IN_PROGRESS, CREATING, CREATED, BYPASSED + UNKNOWN, DOWNLOAD_ERROR, NOT_DOWNLOADED, DOWNLOAD_IN_PROGRESS, DOWNLOADED, ABANDONED, LIMIT_REACHED, UPLOADED, NOT_UPLOADED, UPLOAD_ERROR, UPLOAD_IN_PROGRESS, CREATING, CREATED, BYPASSED } + List ERROR_DOWNLOAD_STATES = List.of(Status.DOWNLOAD_ERROR, Status.ABANDONED, Status.LIMIT_REACHED, Status.UNKNOWN); List PENDING_DOWNLOAD_STATES = List.of(Status.NOT_DOWNLOADED, Status.DOWNLOAD_IN_PROGRESS); String getInstallPath(); diff --git a/api/src/main/java/com/cloud/storage/Volume.java b/api/src/main/java/com/cloud/storage/Volume.java index c7fbdb0a5445..89298e04587f 100644 --- a/api/src/main/java/com/cloud/storage/Volume.java +++ b/api/src/main/java/com/cloud/storage/Volume.java @@ -60,7 +60,9 @@ enum State { UploadError(false, "Volume upload encountered some error"), UploadAbandoned(false, "Volume upload is abandoned since the upload was never initiated within a specified time"), Attaching(true, "The volume is attaching to a VM from Ready state."), - Restoring(true, "The volume is being restored from backup."); + Restoring(true, "The volume is being restored from backup."), + Consolidating(true, "The volume is being flattened."), + RestoreError(false, "The volume restore encountered an error."); boolean _transitional; @@ -153,6 +155,10 @@ public String getDescription() { s_fsm.addTransition(new StateMachine2.Transition(Destroy, Event.RestoreRequested, Restoring, null)); s_fsm.addTransition(new StateMachine2.Transition(Restoring, Event.RestoreSucceeded, Ready, null)); s_fsm.addTransition(new StateMachine2.Transition(Restoring, Event.RestoreFailed, Ready, null)); + s_fsm.addTransition(new StateMachine2.Transition<>(Ready, Event.ConsolidationRequested, Consolidating, null)); + s_fsm.addTransition(new StateMachine2.Transition<>(Consolidating, Event.OperationSucceeded, Ready, null)); + s_fsm.addTransition(new StateMachine2.Transition<>(Consolidating, Event.OperationFailed, RestoreError, null)); + s_fsm.addTransition(new StateMachine2.Transition<>(RestoreError, Event.RestoreFailed, RestoreError, null)); } } @@ -179,7 +185,8 @@ enum Event { OperationTimeout, RestoreRequested, RestoreSucceeded, - RestoreFailed; + RestoreFailed, + ConsolidationRequested } /** @@ -275,6 +282,14 @@ enum Event { void setPassphraseId(Long id); + Long getKmsKeyId(); + + void setKmsKeyId(Long id); + + Long getKmsWrappedKeyId(); + + void setKmsWrappedKeyId(Long id); + String getEncryptFormat(); void setEncryptFormat(String encryptFormat); diff --git a/api/src/main/java/com/cloud/storage/VolumeApiService.java b/api/src/main/java/com/cloud/storage/VolumeApiService.java index 4140d51a800d..372eb0385618 100644 --- a/api/src/main/java/com/cloud/storage/VolumeApiService.java +++ b/api/src/main/java/com/cloud/storage/VolumeApiService.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; +import com.cloud.dc.DataCenter; import com.cloud.exception.ResourceAllocationException; import com.cloud.offering.DiskOffering; import com.cloud.user.Account; @@ -56,9 +57,9 @@ public interface VolumeApiService { Boolean.class, "use.https.to.upload", "true", - "Determines the protocol (HTTPS or HTTP) ACS will use to generate links to upload ISOs, volumes, and templates. When set as 'true', ACS will use protocol HTTPS, otherwise, it will use protocol HTTP. Default value is 'true'.", + "Controls whether upload links for ISOs, volumes, and templates use HTTPS (true, default) or HTTP (false). After changing this setting, the Secondary Storage VM (SSVM) must be recreated", true, - ConfigKey.Scope.StoragePool); + ConfigKey.Scope.Zone); /** * Creates the database object for a volume based on the given criteria @@ -70,6 +71,10 @@ public interface VolumeApiService { */ Volume allocVolume(CreateVolumeCmd cmd) throws ResourceAllocationException; + Volume allocVolume(long ownerId, Long zoneId, Long diskOfferingId, Long vmId, Long snapshotId, String name, + Long cmdSize, Boolean displayVolume, Long cmdMinIops, Long cmdMaxIops, String customId, Long kmsKeyId) + throws ResourceAllocationException; + /** * Creates the volume based on the given criteria * @@ -80,6 +85,8 @@ public interface VolumeApiService { */ Volume createVolume(CreateVolumeCmd cmd); + Volume createVolume(long volumeId, Long vmId, Long snapshotId, Long storageId, Boolean display); + /** * Resizes the volume based on the given criteria * @@ -107,7 +114,7 @@ public interface VolumeApiService { Volume attachVolumeToVM(AttachVolumeCmd command); - Volume attachVolumeToVM(Long vmId, Long volumeId, Long deviceId, Boolean allowAttachForSharedFS); + Volume attachVolumeToVM(Long vmId, Long volumeId, Long deviceId, Boolean allowAttachForSharedFS, boolean allowAttachOnRestoring); Volume detachVolumeViaDestroyVM(long vmId, long volumeId); @@ -180,7 +187,9 @@ Volume updateVolume(long volumeId, String path, String state, Long storageId, */ boolean doesStoragePoolSupportDiskOfferingTags(StoragePool destPool, String diskOfferingTags); - Volume destroyVolume(long volumeId, Account caller, boolean expunge, boolean forceExpunge); + boolean validateConditionsToReplaceDiskOfferingOfVolume(Volume volume, DiskOffering newDiskOffering, StoragePool destPool); + + Volume destroyVolume(long volumeId, Account caller, boolean expunge, boolean forceExpunge, Boolean countDisplayFalseInResourceCount); void destroyVolume(long volumeId); @@ -201,4 +210,6 @@ Volume updateVolume(long volumeId, String path, String state, Long storageId, Pair checkAndRepairVolume(CheckAndRepairVolumeCmd cmd) throws ResourceAllocationException; Long getVolumePhysicalSize(Storage.ImageFormat format, String path, String chainInfo); + + Long getCustomDiskOfferingIdForVolumeUpload(Account owner, DataCenter zone, boolean encryptEnabledOnly); } diff --git a/api/src/main/java/com/cloud/storage/snapshot/SnapshotApiService.java b/api/src/main/java/com/cloud/storage/snapshot/SnapshotApiService.java index 67afd6aa4e24..d52e645ec799 100644 --- a/api/src/main/java/com/cloud/storage/snapshot/SnapshotApiService.java +++ b/api/src/main/java/com/cloud/storage/snapshot/SnapshotApiService.java @@ -85,7 +85,7 @@ public interface SnapshotApiService { * the command that specifies the volume criteria * @return list of snapshot policies */ - Pair, Integer> listPoliciesforVolume(ListSnapshotPoliciesCmd cmd); + Pair, Integer> listSnapshotPolicies(ListSnapshotPoliciesCmd cmd); boolean deleteSnapshotPolicies(DeleteSnapshotPoliciesCmd cmd); diff --git a/api/src/main/java/com/cloud/storage/snapshot/SnapshotPolicy.java b/api/src/main/java/com/cloud/storage/snapshot/SnapshotPolicy.java index 22d5dfb9c1b8..13009a9808aa 100644 --- a/api/src/main/java/com/cloud/storage/snapshot/SnapshotPolicy.java +++ b/api/src/main/java/com/cloud/storage/snapshot/SnapshotPolicy.java @@ -16,11 +16,12 @@ // under the License. package com.cloud.storage.snapshot; +import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.api.Displayable; import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; -public interface SnapshotPolicy extends Identity, InternalIdentity, Displayable { +public interface SnapshotPolicy extends ControlledEntity, Identity, InternalIdentity, Displayable { long getVolumeId(); diff --git a/api/src/main/java/com/cloud/user/AccountService.java b/api/src/main/java/com/cloud/user/AccountService.java index c0ebcf09f59b..fc450e9179c5 100644 --- a/api/src/main/java/com/cloud/user/AccountService.java +++ b/api/src/main/java/com/cloud/user/AccountService.java @@ -21,12 +21,13 @@ import com.cloud.utils.Pair; import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.acl.RolePermissionEntity; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; +import org.apache.cloudstack.acl.apikeypair.ApiKeyPair; +import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission; +import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.command.admin.account.CreateAccountCmd; -import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd; -import org.apache.cloudstack.api.command.admin.user.RegisterCmd; -import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd; import com.cloud.dc.DataCenter; import com.cloud.domain.Domain; @@ -35,7 +36,16 @@ import com.cloud.offering.DiskOffering; import com.cloud.offering.NetworkOffering; import com.cloud.offering.ServiceOffering; +import org.apache.cloudstack.api.command.admin.user.DeleteUserKeysCmd; +import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd; +import org.apache.cloudstack.api.command.admin.user.ListUserKeyRulesCmd; +import org.apache.cloudstack.api.command.admin.user.ListUserKeysCmd; +import org.apache.cloudstack.api.command.admin.user.RegisterUserKeysCmd; +import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd; +import org.apache.cloudstack.api.response.ApiKeyPairResponse; +import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.auth.UserTwoFactorAuthenticator; +import org.apache.cloudstack.backup.BackupOffering; public interface AccountService { @@ -58,7 +68,8 @@ UserAccount createUserAccount(String userName, String password, String firstName User getSystemUser(); - User createUser(String userName, String password, String firstName, String lastName, String email, String timeZone, String accountName, Long domainId, String userUUID); + User createUser(String userName, String password, String firstName, String lastName, String email, String timeZone, + String accountName, Long domainId, String userUUID, boolean isPasswordChangeRequired); User createUser(String userName, String password, String firstName, String lastName, String email, String timeZone, String accountName, Long domainId, String userUUID, User.Source source); @@ -77,10 +88,16 @@ User createUser(String userName, String password, String firstName, String lastN Account getActiveAccountById(long accountId); + Account getActiveAccountByUuid(String accountUuid); + Account getAccount(long accountId); + Account getAccountByUuid(String accountUuid); + User getActiveUser(long userId); + User getOneActiveUserForAccount(Account account); + User getUserIncludingRemoved(long userId); boolean isRootAdmin(Long accountId); @@ -95,7 +112,7 @@ User createUser(String userName, String password, String firstName, String lastN void markUserRegistered(long userId); - public String[] createApiKeyAndSecretKey(RegisterCmd cmd); + ApiKeyPair createApiKeyAndSecretKey(RegisterUserKeysCmd cmd); public String[] createApiKeyAndSecretKey(final long userId); @@ -115,13 +132,19 @@ User createUser(String userName, String password, String firstName, String lastN void checkAccess(Account account, VpcOffering vof, DataCenter zone) throws PermissionDeniedException; + void checkAccess(Account account, BackupOffering bof) throws PermissionDeniedException; + void checkAccess(User user, ControlledEntity entity); void checkAccess(Account account, AccessType accessType, boolean sameOwner, String apiName, ControlledEntity... entities) throws PermissionDeniedException; void validateAccountHasAccessToResource(Account account, AccessType accessType, Object resource); - Long finalyzeAccountId(String accountName, Long domainId, Long projectId, boolean enabledOnly); + void validateCallingUserHasAccessToDesiredUser(Long userId); + + Long finalizeAccountId(String accountName, Long domainId, Long projectId, boolean enabledOnly); + + Long finalizeAccountId(Long accountId, String accountName, Long domainId, Long projectId); /** * returns the user account object for a given user id @@ -130,9 +153,15 @@ User createUser(String userName, String password, String firstName, String lastN */ UserAccount getUserAccountById(Long userId); - public Pair> getKeys(GetUserKeysCmd cmd); + Pair> getKeys(GetUserKeysCmd cmd); + + ListResponse listKeys(ListUserKeysCmd cmd); + + List listKeyRules(ListUserKeyRulesCmd cmd); + + void deleteApiKey(DeleteUserKeysCmd cmd); - public Pair> getKeys(Long userId); + void deleteApiKey(ApiKeyPair id); /** * Lists user two-factor authentication provider plugins @@ -147,4 +176,13 @@ User createUser(String userName, String password, String firstName, String lastN */ UserTwoFactorAuthenticator getUserTwoFactorAuthenticationProvider(final Long domainId); + ApiKeyPair getLatestUserKeyPair(Long userId); + + ApiKeyPair getKeyPairById(Long id); + + ApiKeyPair getKeyPairByApiKey(String apiKey); + + String getAccessingApiKey(BaseCmd cmd); + + List getAllKeypairPermissions(String apiKey); } diff --git a/api/src/main/java/com/cloud/user/ApiKeyPairState.java b/api/src/main/java/com/cloud/user/ApiKeyPairState.java new file mode 100644 index 000000000000..63405c62e320 --- /dev/null +++ b/api/src/main/java/com/cloud/user/ApiKeyPairState.java @@ -0,0 +1,21 @@ +// 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.user; + +public enum ApiKeyPairState { + ENABLED, REMOVED, EXPIRED +} diff --git a/api/src/main/java/com/cloud/user/ResourceLimitService.java b/api/src/main/java/com/cloud/user/ResourceLimitService.java index 49b20fe2fefc..89128f87829e 100644 --- a/api/src/main/java/com/cloud/user/ResourceLimitService.java +++ b/api/src/main/java/com/cloud/user/ResourceLimitService.java @@ -30,11 +30,12 @@ import com.cloud.offering.DiskOffering; import com.cloud.offering.ServiceOffering; import com.cloud.template.VirtualMachineTemplate; +import org.apache.cloudstack.resourcelimit.Reserver; public interface ResourceLimitService { static final ConfigKey MaxAccountSecondaryStorage = new ConfigKey<>("Account Defaults", Long.class, "max.account.secondary.storage", "400", - "The default maximum secondary storage space (in GiB) that can be used for an account", false); + "The default maximum secondary storage space (in GiB) that can be used for an Account", false); static final ConfigKey MaxProjectSecondaryStorage = new ConfigKey<>("Project Defaults", Long.class, "max.project.secondary.storage", "400", "The default maximum secondary storage space (in GiB) that can be used for a project", false); static final ConfigKey ResourceCountCheckInterval = new ConfigKey<>("Advanced", Long.class, "resourcecount.check.interval", "300", @@ -191,6 +192,7 @@ public interface ResourceLimitService { */ public void checkResourceLimit(Account account, ResourceCount.ResourceType type, long... count) throws ResourceAllocationException; public void checkResourceLimitWithTag(Account account, ResourceCount.ResourceType type, String tag, long... count) throws ResourceAllocationException; + public void checkResourceLimitWithTag(Account account, Long domainId, boolean considerSystemAccount, ResourceCount.ResourceType type, String tag, long... count) throws ResourceAllocationException; /** * Gets the count of resources for a resource type and account @@ -251,15 +253,15 @@ public interface ResourceLimitService { List getResourceLimitStorageTags(DiskOffering diskOffering); void updateTaggedResourceLimitsAndCountsForAccounts(List responses, String tag); void updateTaggedResourceLimitsAndCountsForDomains(List responses, String tag); - void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException; - + void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering, List reservations) throws ResourceAllocationException; + List getResourceLimitStorageTagsForResourceCountOperation(Boolean display, DiskOffering diskOffering, Boolean enforceResourceLimitOnDisplayFalse); void checkVolumeResourceLimitForDiskOfferingChange(Account owner, Boolean display, Long currentSize, Long newSize, - DiskOffering currentOffering, DiskOffering newOffering) throws ResourceAllocationException; + DiskOffering currentOffering, DiskOffering newOffering, List reservations) throws ResourceAllocationException; - void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException; + void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering, List reservations) throws ResourceAllocationException; void incrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering); - void decrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering); + void decrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering, Boolean countDisplayFalseInResourceCount); void updateVmResourceCountForTemplateChange(long accountId, Boolean display, ServiceOffering offering, VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate); @@ -273,25 +275,23 @@ void updateVolumeResourceCountForDiskOfferingChange(long accountId, Boolean disp void incrementVolumePrimaryStorageResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering); void decrementVolumePrimaryStorageResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering); - void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template) throws ResourceAllocationException; - void incrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template); - void decrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template); + void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, List reservations) throws ResourceAllocationException; + void incrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Boolean countDisplayFalseInResourceLimit); + void decrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Boolean countDisplayFalseInResourceCount); void checkVmResourceLimitsForServiceOfferingChange(Account owner, Boolean display, Long currentCpu, Long newCpu, - Long currentMemory, Long newMemory, ServiceOffering currentOffering, ServiceOffering newOffering, VirtualMachineTemplate template) throws ResourceAllocationException; + Long currentMemory, Long newMemory, ServiceOffering currentOffering, ServiceOffering newOffering, VirtualMachineTemplate template, List reservations) throws ResourceAllocationException; void checkVmResourceLimitsForTemplateChange(Account owner, Boolean display, ServiceOffering offering, - VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate) throws ResourceAllocationException; + VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate, List reservations) throws ResourceAllocationException; - void checkVmCpuResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu) throws ResourceAllocationException; void incrementVmCpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu); void decrementVmCpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu); - void checkVmMemoryResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory) throws ResourceAllocationException; 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); + long recalculateDomainResourceCount(final long domainId, final ResourceType type, String tag); } diff --git a/api/src/main/java/com/cloud/user/User.java b/api/src/main/java/com/cloud/user/User.java index 041b39ad2729..da7245a47980 100644 --- a/api/src/main/java/com/cloud/user/User.java +++ b/api/src/main/java/com/cloud/user/User.java @@ -65,14 +65,6 @@ public enum Source { public void setState(Account.State state); - public String getApiKey(); - - public void setApiKey(String apiKey); - - public String getSecretKey(); - - public void setSecretKey(String secretKey); - public String getTimezone(); public void setTimezone(String timezone); diff --git a/api/src/main/java/com/cloud/user/UserAccount.java b/api/src/main/java/com/cloud/user/UserAccount.java index e6b07fb371eb..5736244e3259 100644 --- a/api/src/main/java/com/cloud/user/UserAccount.java +++ b/api/src/main/java/com/cloud/user/UserAccount.java @@ -39,10 +39,6 @@ public interface UserAccount extends InternalIdentity { String getState(); - String getApiKey(); - - String getSecretKey(); - Date getCreated(); Date getRemoved(); diff --git a/api/src/main/java/com/cloud/vm/DiskProfile.java b/api/src/main/java/com/cloud/vm/DiskProfile.java index 971ebde496e4..766573d4d40b 100644 --- a/api/src/main/java/com/cloud/vm/DiskProfile.java +++ b/api/src/main/java/com/cloud/vm/DiskProfile.java @@ -82,7 +82,7 @@ public DiskProfile(Volume vol, DiskOffering offering, HypervisorType hyperType) null); this.hyperType = hyperType; this.provisioningType = offering.getProvisioningType(); - this.requiresEncryption = offering.getEncrypt() || vol.getPassphraseId() != null; + this.requiresEncryption = offering.getEncrypt() || vol.getPassphraseId() != null || vol.getKmsKeyId() != null; } public DiskProfile(DiskProfile dp) { diff --git a/api/src/main/java/com/cloud/vm/Nic.java b/api/src/main/java/com/cloud/vm/Nic.java index afc44b8d39fa..3722e5769c92 100644 --- a/api/src/main/java/com/cloud/vm/Nic.java +++ b/api/src/main/java/com/cloud/vm/Nic.java @@ -33,6 +33,11 @@ * Nic represents one nic on the VM. */ public interface Nic extends Identity, InternalIdentity { + + interface Topics { + String NIC_LIFECYCLE = "nic.lifecycle"; + } + enum Event { ReservationRequested, ReleaseRequested, CancelRequested, OperationCompleted, OperationFailed, } @@ -162,4 +167,6 @@ public enum ReservationStrategy { String getIPv6Address(); Integer getMtu(); + + boolean isEnabled(); } diff --git a/api/src/main/java/com/cloud/vm/NicProfile.java b/api/src/main/java/com/cloud/vm/NicProfile.java index a0c80ceb1bfb..54a32bbcb181 100644 --- a/api/src/main/java/com/cloud/vm/NicProfile.java +++ b/api/src/main/java/com/cloud/vm/NicProfile.java @@ -52,6 +52,7 @@ public class NicProfile implements InternalIdentity, Serializable { boolean defaultNic; Integer networkRate; boolean isSecurityGroupEnabled; + boolean enabled; Integer orderIndex; @@ -87,6 +88,7 @@ public NicProfile(Nic nic, Network network, URI broadcastUri, URI isolationUri, broadcastType = network.getBroadcastDomainType(); trafficType = network.getTrafficType(); format = nic.getAddressFormat(); + enabled = nic.isEnabled(); iPv4Address = nic.getIPv4Address(); iPv4Netmask = nic.getIPv4Netmask(); @@ -414,6 +416,14 @@ public void setIpv4AllocationRaceCheck(boolean ipv4AllocationRaceCheck) { this.ipv4AllocationRaceCheck = ipv4AllocationRaceCheck; } + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + // // OTHER METHODS // @@ -453,6 +463,6 @@ public String toString() { return String.format("NicProfile %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( this, "id", "uuid", "vmId", "deviceId", - "broadcastUri", "reservationId", "iPv4Address")); + "broadcastType", "broadcastUri", "reservationId", "iPv4Address")); } } diff --git a/api/src/main/java/com/cloud/vm/NicSecondaryIp.java b/api/src/main/java/com/cloud/vm/NicSecondaryIp.java index 2856e0aea756..d25627c1782d 100644 --- a/api/src/main/java/com/cloud/vm/NicSecondaryIp.java +++ b/api/src/main/java/com/cloud/vm/NicSecondaryIp.java @@ -38,6 +38,8 @@ public interface NicSecondaryIp extends ControlledEntity, Identity, InternalIden String getIp6Address(); + String getDescription(); + long getNetworkId(); long getVmId(); diff --git a/api/src/main/java/com/cloud/vm/UserVmService.java b/api/src/main/java/com/cloud/vm/UserVmService.java index 6f1aba4613da..5864e91cd7f4 100644 --- a/api/src/main/java/com/cloud/vm/UserVmService.java +++ b/api/src/main/java/com/cloud/vm/UserVmService.java @@ -40,6 +40,7 @@ import org.apache.cloudstack.api.command.user.vm.StartVMCmd; import org.apache.cloudstack.api.command.user.vm.UpdateDefaultNicForVMCmd; import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd; +import org.apache.cloudstack.api.command.user.vm.UpdateVmNicCmd; import org.apache.cloudstack.api.command.user.vm.UpdateVmNicIpCmd; import org.apache.cloudstack.api.command.user.vm.UpgradeVMCmd; import org.apache.cloudstack.api.command.user.vmgroup.CreateVMGroupCmd; @@ -64,6 +65,7 @@ import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.Account; import com.cloud.uservm.UserVm; +import com.cloud.utils.Pair; import com.cloud.utils.exception.ExecutionException; public interface UserVmService { @@ -73,10 +75,11 @@ public interface UserVmService { * Destroys one virtual machine * * @param cmd the API Command Object containg the parameters to use for this service action + * @param checkExpunge * @throws ConcurrentOperationException * @throws ResourceUnavailableException */ - UserVm destroyVm(DestroyVMCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException; + UserVm destroyVm(DestroyVMCmd cmd, boolean checkExpunge) throws ResourceUnavailableException, ConcurrentOperationException; /** * Destroys one virtual machine @@ -151,6 +154,8 @@ void startVirtualMachineForHA(VirtualMachine vm, Map 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, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, + Map userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, Long rootDiskKmsKeyId, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; /** @@ -301,7 +306,7 @@ UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOfferin 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, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; + Map dataDiskTemplateToDiskOfferingMap, Map userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, Long rootDiskKmsKeyId, String vmType, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; /** * Creates a User VM in Advanced Zone (Security Group feature is disabled) @@ -373,7 +378,7 @@ UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffe 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, Volume volume, Snapshot snapshot) + Map templateOvfPropertiesMap, boolean dynamicScalingEnabled, String vmType, Long overrideDiskOfferingId, Long rootDiskKmsKeyId, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; @@ -507,16 +512,42 @@ UserVm upgradeVirtualMachine(ScaleVMCmd cmd) throws ResourceUnavailableException void collectVmNetworkStatistics (UserVm userVm); - UserVm importVM(final DataCenter zone, final Host host, final VirtualMachineTemplate template, final String instanceName, final String displayName, final Account owner, final String userData, final Account caller, final Boolean isDisplayVm, final String keyboard, - final long accountId, final long userId, final ServiceOffering serviceOffering, final String sshPublicKey, + /** + * Import VM into CloudStack + * @param zone importing zone + * @param host importing host + * @param template template for the imported VM + * @param instanceNameInternal set to null to CloudStack to autogenerate from the next available VM ID on database + * @param displayName display name for the imported VM + * @param owner owner of the imported VM + * @param userData user data for the imported VM + * @param caller caller account + * @param isDisplayVm true to display the imported VM + * @param keyboard keyboard distribution for the imported VM + * @param accountId account ID + * @param userId user ID + * @param serviceOffering service offering for the imported VM + * @param sshPublicKey ssh key for the imported VM + * @param guestOsId guest OS ID for the imported VM (if not passed, then the guest OS of the template will be used) + * @param hostName the name for the imported VM + * @param hypervisorType hypervisor type for the imported VM + * @param customParameters details for the imported VM + * @param powerState power state of the imported VM + * @param networkNicMap network to nic mapping + * @return the imported VM + * @throws InsufficientCapacityException in case of errors + */ + UserVm importVM(final DataCenter zone, final Host host, final VirtualMachineTemplate template, final String instanceNameInternal, final String displayName, final Account owner, final String userData, final Account caller, final Boolean isDisplayVm, final String keyboard, + final long accountId, final long userId, final ServiceOffering serviceOffering, final String sshPublicKey, final Long guestOsId, final String hostName, final HypervisorType hypervisorType, final Map customParameters, final VirtualMachine.PowerState powerState, final LinkedHashMap> networkNicMap) throws InsufficientCapacityException; /** * Unmanage a guest VM from CloudStack - * @return true if the VM is successfully unmanaged, false if not. + * + * @return (true if successful, false if not, hostUuid) if the VM is successfully unmanaged. */ - boolean unmanageUserVM(Long vmId); + Pair unmanageUserVM(Long vmId, Long targetHostId); UserVm allocateVMFromBackup(CreateVMFromBackupCmd cmd) throws InsufficientCapacityException, ResourceAllocationException, ResourceUnavailableException; diff --git a/api/src/main/java/com/cloud/vm/VirtualMachine.java b/api/src/main/java/com/cloud/vm/VirtualMachine.java index d244de7115e8..3adcc85d28a1 100644 --- a/api/src/main/java/com/cloud/vm/VirtualMachine.java +++ b/api/src/main/java/com/cloud/vm/VirtualMachine.java @@ -58,7 +58,10 @@ public enum State { Error(false, "VM is in error"), Unknown(false, "VM state is unknown."), Shutdown(false, "VM state is shutdown from inside"), - Restoring(true, "VM is being restored from backup"); + Restoring(true, "VM is being restored from backup"), + BackingUp(true, "VM is being backed up"), + BackupError(false, "VM backup is in an inconsistent state. Operator should analyse the logs and restore the VM"), + RestoreError(false, "VM restore left the VM in an inconsistent state. Operator should analyse the logs and restore the VM"); private final boolean _transitional; String _description; @@ -124,6 +127,9 @@ public static StateMachine2 getStat s_fsm.addTransition(new Transition(State.Stopping, VirtualMachine.Event.StopRequested, State.Stopping, null)); s_fsm.addTransition(new Transition(State.Stopping, VirtualMachine.Event.AgentReportShutdowned, State.Stopped, Arrays.asList(new Impact[]{Impact.USAGE}))); s_fsm.addTransition(new Transition(State.Expunging, VirtualMachine.Event.OperationFailed, State.Expunging,null)); + // Note: In addition to the Stopped -> Error transition for failed VM creation, + // a VM can also transition from Expunging to Error on OperationFailedToError. + s_fsm.addTransition(new Transition(State.Expunging, VirtualMachine.Event.OperationFailedToError, State.Error, null)); s_fsm.addTransition(new Transition(State.Expunging, VirtualMachine.Event.ExpungeOperation, State.Expunging,null)); 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)); @@ -131,6 +137,14 @@ public static StateMachine2 getStat 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)); + s_fsm.addTransition(new Transition<>(State.Running, Event.BackupRequested, State.BackingUp, null)); + s_fsm.addTransition(new Transition<>(State.Stopped, Event.BackupRequested, State.BackingUp, null)); + s_fsm.addTransition(new Transition<>(State.BackingUp, Event.BackupSucceededRunning, State.Running, null)); + s_fsm.addTransition(new Transition<>(State.BackingUp, Event.BackupSucceededStopped, State.Stopped, null)); + s_fsm.addTransition(new Transition<>(State.BackingUp, Event.OperationFailedToError, State.BackupError, null)); + s_fsm.addTransition(new Transition<>(State.BackingUp, Event.OperationFailedToRunning, State.Running, null)); + s_fsm.addTransition(new Transition<>(State.BackingUp, Event.OperationFailedToStopped, State.Stopped, null)); + s_fsm.addTransition(new Transition(State.RestoreError, Event.RestoringFailed, State.RestoreError, null)); s_fsm.addTransition(new Transition(State.Starting, VirtualMachine.Event.FollowAgentPowerOnReport, State.Running, Arrays.asList(new Impact[]{Impact.USAGE}))); s_fsm.addTransition(new Transition(State.Stopping, VirtualMachine.Event.FollowAgentPowerOnReport, State.Running, null)); @@ -209,6 +223,8 @@ public enum Event { ExpungeOperation, OperationSucceeded, OperationFailed, + OperationFailedToRunning, + OperationFailedToStopped, OperationFailedToError, OperationRetry, AgentReportShutdowned, @@ -218,6 +234,10 @@ public enum Event { RestoringRequested, RestoringFailed, RestoringSuccess, + BackupRequested, + BackupSucceededStopped, + BackupSucceededRunning, + FinalizedBackupChain, // added for new VMSync logic FollowAgentPowerOnReport, diff --git a/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java b/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java index c67ee4eabc28..5c78d6bedd64 100644 --- a/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java +++ b/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java @@ -78,6 +78,7 @@ public static class Param { public static final Param BootIntoSetup = new Param("enterHardwareSetup"); public static final Param PreserveNics = new Param("PreserveNics"); public static final Param ConsiderLastHost = new Param("ConsiderLastHost"); + public static final Param ReturnAfterVolumePrepare = new Param("ReturnAfterVolumePrepare"); private String name; diff --git a/api/src/main/java/com/cloud/vm/VmDetailConstants.java b/api/src/main/java/com/cloud/vm/VmDetailConstants.java index ea5d209a5d41..877df55c6d67 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"; @@ -54,6 +55,9 @@ public interface VmDetailConstants { String NIC_MULTIQUEUE_NUMBER = "nic.multiqueue.number"; String NIC_PACKED_VIRTQUEUES_ENABLED = "nic.packed.virtqueues.enabled"; + // KVM specific, disk controllers + String KVM_SKIP_FORCE_DISK_CONTROLLER = "skip.force.disk.controller"; + // Mac OSX guest specific (internal) String SMC_PRESENT = "smc.present"; String FIRMWARE = "firmware"; @@ -92,6 +96,7 @@ public interface VmDetailConstants { String CKS_NODE_TYPE = "node"; String OFFERING = "offering"; String TEMPLATE = "template"; + String AFFINITY_GROUP = "affinitygroup"; // VMware to KVM VM migrations specific String VMWARE_TO_KVM_PREFIX = "vmware-to-kvm"; @@ -125,4 +130,20 @@ public interface VmDetailConstants { String EXTERNAL_DETAIL_PREFIX = "External:"; String CLOUDSTACK_VM_DETAILS = "cloudstack.vm.details"; String CLOUDSTACK_VLAN = "cloudstack.vlan"; + + // KVM Checkpoints related + String ACTIVE_CHECKPOINT_ID = "active.checkpoint.id"; + String ACTIVE_CHECKPOINT_CREATE_TIME = "active.checkpoint.create.time"; + String LAST_CHECKPOINT_ID = "last.checkpoint.id"; + String LAST_CHECKPOINT_CREATE_TIME = "last.checkpoint.create.time"; + + // KBOSS specific + String LINKED_VOLUMES_SECONDARY_STORAGE_UUIDS = "linkedVolumesSecondaryStorageUuids"; + String VALIDATION_COMMAND = "backupValidationCommand"; + String VALIDATION_COMMAND_ARGUMENTS = "backupValidationCommandArguments"; + String VALIDATION_COMMAND_EXPECTED_RESULT = "backupValidationCommandExpectedResult"; + String VALIDATION_COMMAND_TIMEOUT = "backupValidationCommandTimeout"; + String VALIDATION_SCREENSHOT_WAIT = "backupValidationScreenshotWait"; + String VALIDATION_BOOT_TIMEOUT = "backupValidationBootTimeout"; + String LAST_KNOWN_STATE = "last_known_state"; } diff --git a/api/src/main/java/com/cloud/vm/VmDiskInfo.java b/api/src/main/java/com/cloud/vm/VmDiskInfo.java index b8779a8d77c6..97683e8397fa 100644 --- a/api/src/main/java/com/cloud/vm/VmDiskInfo.java +++ b/api/src/main/java/com/cloud/vm/VmDiskInfo.java @@ -33,6 +33,11 @@ public VmDiskInfo(DiskOffering diskOffering, Long size, Long minIops, Long maxIo _deviceId = deviceId; } + public VmDiskInfo(DiskOffering diskOffering, Long size, Long minIops, Long maxIops, Long deviceId, Long kmsKeyId) { + super(diskOffering, size, minIops, maxIops, kmsKeyId); + _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 24e93af15621..b4b144e2f8af 100644 --- a/api/src/main/java/com/cloud/vm/snapshot/VMSnapshot.java +++ b/api/src/main/java/com/cloud/vm/snapshot/VMSnapshot.java @@ -29,10 +29,10 @@ public interface VMSnapshot extends ControlledEntity, Identity, InternalIdentity, StateObject { 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( + Allocated("The Instance Snapshot is allocated but has not been created yet."), Creating("The Instance Snapshot is being created."), Ready( + "The Instance Snapshot is ready to be used."), Reverting("The Instance 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"), - Hidden("The VM snapshot is hidden from the user and cannot be recovered."); + Hidden("The Instance snapshot is hidden from the user and cannot be recovered."); String _description; diff --git a/api/src/main/java/com/cloud/vm/snapshot/VMSnapshotService.java b/api/src/main/java/com/cloud/vm/snapshot/VMSnapshotService.java index 84a56aaedd34..754e463e7101 100644 --- a/api/src/main/java/com/cloud/vm/snapshot/VMSnapshotService.java +++ b/api/src/main/java/com/cloud/vm/snapshot/VMSnapshotService.java @@ -19,6 +19,7 @@ import java.util.List; +import com.cloud.utils.fsm.NoTransitionException; import org.apache.cloudstack.api.command.user.vmsnapshot.ListVMSnapshotCmd; import com.cloud.exception.ConcurrentOperationException; @@ -53,4 +54,6 @@ UserVm revertToSnapshot(Long vmSnapshotId) throws InsufficientServerCapacityExce * @param id vm id */ boolean deleteVMSnapshotsFromDB(Long vmId, boolean unmanage); + + void updateOperationFailed(VMSnapshot vmSnapshot) throws NoTransitionException; } diff --git a/api/src/main/java/org/apache/cloudstack/acl/APIChecker.java b/api/src/main/java/org/apache/cloudstack/acl/APIChecker.java index 660f64f43ef2..286a3598e4fb 100644 --- a/api/src/main/java/org/apache/cloudstack/acl/APIChecker.java +++ b/api/src/main/java/org/apache/cloudstack/acl/APIChecker.java @@ -20,6 +20,7 @@ import com.cloud.user.Account; import com.cloud.user.User; import com.cloud.utils.component.Adapter; +import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission; import java.util.List; @@ -31,8 +32,8 @@ public interface APIChecker extends Adapter { // If true, apiChecker has checked the operation // If false, apiChecker is unable to handle the operation or not implemented // On exception, checkAccess failed don't allow - boolean checkAccess(User user, String apiCommandName) throws PermissionDeniedException; - boolean checkAccess(Account account, String apiCommandName) throws PermissionDeniedException; + boolean checkAccess(User user, String apiCommandName, ApiKeyPairPermission... apiKeyPairPermissions) throws PermissionDeniedException; + boolean checkAccess(Account account, String apiCommandName, ApiKeyPairPermission... apiKeyPairPermissions) throws PermissionDeniedException; /** * Verifies if the account has permission for the given list of APIs and returns only the allowed ones. * @@ -43,4 +44,5 @@ public interface APIChecker extends Adapter { */ List getApisAllowedToUser(Role role, User user, List apiNames) throws PermissionDeniedException; boolean isEnabled(); + List getImplicitRolePermissions(RoleType roleType); } diff --git a/api/src/main/java/org/apache/cloudstack/acl/RolePermissionEntity.java b/api/src/main/java/org/apache/cloudstack/acl/RolePermissionEntity.java index 251c6b6d3f9e..f382b1c6964f 100644 --- a/api/src/main/java/org/apache/cloudstack/acl/RolePermissionEntity.java +++ b/api/src/main/java/org/apache/cloudstack/acl/RolePermissionEntity.java @@ -21,7 +21,7 @@ import org.apache.cloudstack.api.InternalIdentity; public interface RolePermissionEntity extends InternalIdentity, Identity { - public enum Permission { + enum Permission { ALLOW, DENY } Rule getRule(); diff --git a/api/src/main/java/org/apache/cloudstack/acl/RoleService.java b/api/src/main/java/org/apache/cloudstack/acl/RoleService.java index f041c8342aec..14e0a608a925 100644 --- a/api/src/main/java/org/apache/cloudstack/acl/RoleService.java +++ b/api/src/main/java/org/apache/cloudstack/acl/RoleService.java @@ -104,5 +104,26 @@ public interface RoleService { List findAllPermissionsBy(Long roleId); + List findAllRolePermissionsEntityBy(Long roleId, boolean considerImplicitRules); + Permission getRolePermission(String permission); + + int removeRolesIfNeeded(List roles); + + /** + * Checks if the role of the caller account has compatible permissions of the specified role permissions. + * For each permission of the {@param rolePermissionsToAccess}, the role of the caller needs to contain the same permission. + * + * @param rolePermissions the permissions of the caller role. + * @param rolePermissionsToAccess the permissions for the role that the caller role wants to access. + * @return True if the role can be accessed with the given permissions; false otherwise. + */ + boolean roleHasPermission(Map rolePermissions, List rolePermissionsToAccess); + + /** + * Given a list of role permissions, returns a {@link Map} containing the API name as the key and the {@link RolePermissionEntity} for the API as the value. + * + * @param rolePermissions Permissions for the role from role. + */ + Map getRoleRulesAndPermissions(List rolePermissions); } 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 46e4f1bc510d..c33488cd9239 100644 --- a/api/src/main/java/org/apache/cloudstack/acl/RoleType.java +++ b/api/src/main/java/org/apache/cloudstack/acl/RoleType.java @@ -132,10 +132,10 @@ public static Set fromCombinedMask(int combinedMask) { * */ public static Account.Type getAccountTypeByRole(final Role role, final Account.Type defautAccountType) { if (role != null) { - LOGGER.debug(String.format("Role [%s] is not null; therefore, we use its account type [%s].", role, defautAccountType)); + LOGGER.debug("Role [{}] is not null; therefore, we use its Account type [{}].", role, defautAccountType); return role.getRoleType().getAccountType(); } - LOGGER.debug(String.format("Role is null; therefore, we use the default account type [%s] value.", defautAccountType)); + LOGGER.debug("Role is null; therefore, we use the default Account type [{}] value.", defautAccountType); return defautAccountType; } } diff --git a/api/src/main/java/org/apache/cloudstack/acl/Rule.java b/api/src/main/java/org/apache/cloudstack/acl/Rule.java index a4ef7773f67b..ad01825a95f1 100644 --- a/api/src/main/java/org/apache/cloudstack/acl/Rule.java +++ b/api/src/main/java/org/apache/cloudstack/acl/Rule.java @@ -25,16 +25,18 @@ public final class Rule { private final String rule; + private final Pattern matchingPattern; private final static Pattern ALLOWED_PATTERN = Pattern.compile("^[a-zA-Z0-9*]+$"); public Rule(final String rule) { validate(rule); this.rule = rule; + matchingPattern = Pattern.compile(rule.toLowerCase().replace("*", "(\\w*\\*?)+")); } public boolean matches(final String commandName) { - return StringUtils.isNotEmpty(commandName) - && commandName.toLowerCase().matches(rule.toLowerCase().replace("*", "\\w*")); + return StringUtils.isNotEmpty(commandName) && + matchingPattern.matcher(commandName.toLowerCase()).matches(); } public String getRuleString() { diff --git a/api/src/main/java/org/apache/cloudstack/acl/SecurityChecker.java b/api/src/main/java/org/apache/cloudstack/acl/SecurityChecker.java index 82a8ec5fe932..fa17df7c6ed4 100644 --- a/api/src/main/java/org/apache/cloudstack/acl/SecurityChecker.java +++ b/api/src/main/java/org/apache/cloudstack/acl/SecurityChecker.java @@ -27,6 +27,8 @@ import com.cloud.user.User; import com.cloud.utils.component.Adapter; +import org.apache.cloudstack.backup.BackupOffering; + /** * SecurityChecker checks the ownership and access control to objects within */ @@ -145,4 +147,6 @@ boolean checkAccess(Account caller, AccessType accessType, String action, Contro boolean checkAccess(Account account, NetworkOffering nof, DataCenter zone) throws PermissionDeniedException; boolean checkAccess(Account account, VpcOffering vof, DataCenter zone) throws PermissionDeniedException; + + boolean checkAccess(Account account, BackupOffering bof) throws PermissionDeniedException; } diff --git a/api/src/main/java/org/apache/cloudstack/acl/apikeypair/ApiKeyPair.java b/api/src/main/java/org/apache/cloudstack/acl/apikeypair/ApiKeyPair.java new file mode 100644 index 000000000000..ecce0ae50824 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/acl/apikeypair/ApiKeyPair.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.acl.apikeypair; + +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +public interface ApiKeyPair extends ControlledEntity, InternalIdentity, Identity { + Long getUserId(); + Date getStartDate(); + Date getEndDate(); + Date getCreated(); + String getDescription(); + String getApiKey(); + String getSecretKey(); + String getName(); + Date getRemoved(); + void setRemoved(Date date); + void validateDate(); + boolean hasEndDatePassed(); +} diff --git a/api/src/main/java/org/apache/cloudstack/acl/apikeypair/ApiKeyPairPermission.java b/api/src/main/java/org/apache/cloudstack/acl/apikeypair/ApiKeyPairPermission.java new file mode 100644 index 000000000000..60b3834cc073 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/acl/apikeypair/ApiKeyPairPermission.java @@ -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. +package org.apache.cloudstack.acl.apikeypair; + +import org.apache.cloudstack.acl.RolePermissionEntity; + +public interface ApiKeyPairPermission extends RolePermissionEntity { + long getApiKeyPairId(); +} diff --git a/api/src/main/java/org/apache/cloudstack/acl/apikeypair/ApiKeyPairService.java b/api/src/main/java/org/apache/cloudstack/acl/apikeypair/ApiKeyPairService.java new file mode 100644 index 000000000000..de9c829b17dc --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/acl/apikeypair/ApiKeyPairService.java @@ -0,0 +1,27 @@ +// 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.acl.apikeypair; + +import java.util.List; + +public interface ApiKeyPairService { + List findAllPermissionsByKeyPairId(Long apiKeyPairId, Long roleId); + + ApiKeyPair findByApiKey(String apiKey); + + ApiKeyPair findById(Long id); +} diff --git a/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupResponse.java b/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupResponse.java index 69f391a5656a..f5a71b994525 100644 --- a/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupResponse.java +++ b/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupResponse.java @@ -34,27 +34,27 @@ public class AffinityGroupResponse extends BaseResponse implements ControlledViewEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the affinity group") + @Param(description = "The ID of the affinity group") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the affinity group") + @Param(description = "The name of the affinity group") private String name; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "the description of the affinity group") + @Param(description = "The description of the affinity group") private String description; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account owning the affinity group") + @Param(description = "The account owning the affinity group") private String accountName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID of the affinity group") + @Param(description = "The domain ID of the affinity group") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name of the affinity group") + @Param(description = "The domain name of the affinity group") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) @@ -62,19 +62,19 @@ public class AffinityGroupResponse extends BaseResponse implements ControlledVie private String domainPath; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project ID of the affinity group") + @Param(description = "The project ID of the affinity group") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the affinity group") + @Param(description = "The project name of the affinity group") private String projectName; @SerializedName(ApiConstants.TYPE) - @Param(description = "the type of the affinity group") + @Param(description = "The type of the affinity group") private String type; @SerializedName("virtualmachineIds") - @Param(description = "virtual machine IDs associated with this affinity group") + @Param(description = "Instance IDs associated with this affinity group") private List vmIdList; @SerializedName("dedicatedresources") diff --git a/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupService.java b/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupService.java index 018e5f5bab5a..03992c0c1c7c 100644 --- a/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupService.java +++ b/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupService.java @@ -66,5 +66,4 @@ public interface AffinityGroupService { boolean isAffinityGroupAvailableInDomain(long affinityGroupId, long domainId); - } diff --git a/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupTypeResponse.java b/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupTypeResponse.java index 6f5fb23d159e..7ddf6dd9f73f 100644 --- a/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupTypeResponse.java +++ b/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupTypeResponse.java @@ -29,7 +29,7 @@ public class AffinityGroupTypeResponse extends BaseResponse { @SerializedName(ApiConstants.TYPE) - @Param(description = "the type of the affinity group") + @Param(description = "The type of the affinity group") private String type; public AffinityGroupTypeResponse() { diff --git a/api/src/main/java/org/apache/cloudstack/affinity/AffinityProcessorBase.java b/api/src/main/java/org/apache/cloudstack/affinity/AffinityProcessorBase.java index 9995d8039e1f..96ca35f264ca 100644 --- a/api/src/main/java/org/apache/cloudstack/affinity/AffinityProcessorBase.java +++ b/api/src/main/java/org/apache/cloudstack/affinity/AffinityProcessorBase.java @@ -29,6 +29,9 @@ public class AffinityProcessorBase extends AdapterBase implements AffinityGroupProcessor { + public static final String AFFINITY_TYPE_HOST = "host affinity"; + public static final String AFFINITY_TYPE_HOST_ANTI = "host anti-affinity"; + protected String _type; @Override 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 d8e471756a02..a9c2abc11ce7 100644 --- a/api/src/main/java/org/apache/cloudstack/alert/AlertService.java +++ b/api/src/main/java/org/apache/cloudstack/alert/AlertService.java @@ -24,18 +24,24 @@ public interface AlertService { public static class AlertType { - private static Set defaultAlertTypes = new HashSet(); + private static final Set defaultAlertTypes = new HashSet<>(); private final String name; private final short type; + private final boolean repetitionAllowed; - private AlertType(short type, String name, boolean isDefault) { + private AlertType(short type, String name, boolean isDefault, boolean repetitionAllowed) { this.name = name; this.type = type; + this.repetitionAllowed = repetitionAllowed; if (isDefault) { defaultAlertTypes.add(this); } } + private AlertType(short type, String name, boolean isDefault) { + this(type, name, isDefault, false); + } + public static final AlertType ALERT_TYPE_MEMORY = new AlertType(Capacity.CAPACITY_TYPE_MEMORY, "ALERT.MEMORY", true); public static final AlertType ALERT_TYPE_CPU = new AlertType(Capacity.CAPACITY_TYPE_CPU, "ALERT.CPU", true); public static final AlertType ALERT_TYPE_STORAGE = new AlertType(Capacity.CAPACITY_TYPE_STORAGE, "ALERT.STORAGE", true); @@ -45,37 +51,41 @@ private AlertType(short type, String name, boolean isDefault) { public static final AlertType ALERT_TYPE_VIRTUAL_NETWORK_IPV6_SUBNET = new AlertType(Capacity.CAPACITY_TYPE_VIRTUAL_NETWORK_IPV6_SUBNET, "ALERT.NETWORK.IPV6SUBNET", true); public static final AlertType ALERT_TYPE_PRIVATE_IP = new AlertType(Capacity.CAPACITY_TYPE_PRIVATE_IP, "ALERT.NETWORK.PRIVATEIP", true); public static final AlertType ALERT_TYPE_SECONDARY_STORAGE = new AlertType(Capacity.CAPACITY_TYPE_SECONDARY_STORAGE, "ALERT.STORAGE.SECONDARY", true); - public static final AlertType ALERT_TYPE_HOST = new AlertType((short)7, "ALERT.COMPUTE.HOST", true); - public static final AlertType ALERT_TYPE_USERVM = new AlertType((short)8, "ALERT.USERVM", true); - public static final AlertType ALERT_TYPE_DOMAIN_ROUTER = new AlertType((short)9, "ALERT.SERVICE.DOMAINROUTER", true); - public static final AlertType ALERT_TYPE_CONSOLE_PROXY = new AlertType((short)10, "ALERT.SERVICE.CONSOLEPROXY", true); + public static final AlertType ALERT_TYPE_HOST = new AlertType((short)7, "ALERT.COMPUTE.HOST", true, true); + public static final AlertType ALERT_TYPE_USERVM = new AlertType((short)8, "ALERT.USERVM", true, true); + public static final AlertType ALERT_TYPE_DOMAIN_ROUTER = new AlertType((short)9, "ALERT.SERVICE.DOMAINROUTER", true, true); + public static final AlertType ALERT_TYPE_CONSOLE_PROXY = new AlertType((short)10, "ALERT.SERVICE.CONSOLEPROXY", true, true); public static final AlertType ALERT_TYPE_ROUTING = new AlertType((short)11, "ALERT.NETWORK.ROUTING", true); - public static final AlertType ALERT_TYPE_STORAGE_MISC = new AlertType((short)12, "ALERT.STORAGE.MISC", true); + public static final AlertType ALERT_TYPE_STORAGE_MISC = new AlertType((short)12, "ALERT.STORAGE.MISC", true, true); public static final AlertType ALERT_TYPE_USAGE_SERVER = new AlertType((short)13, "ALERT.USAGE", true); - public static final AlertType ALERT_TYPE_MANAGEMENT_NODE = new AlertType((short)14, "ALERT.MANAGEMENT", true); + public static final AlertType ALERT_TYPE_MANAGEMENT_NODE = new AlertType((short)14, "ALERT.MANAGEMENT", true, true); public static final AlertType ALERT_TYPE_DOMAIN_ROUTER_MIGRATE = new AlertType((short)15, "ALERT.NETWORK.DOMAINROUTERMIGRATE", true); public static final AlertType ALERT_TYPE_CONSOLE_PROXY_MIGRATE = new AlertType((short)16, "ALERT.SERVICE.CONSOLEPROXYMIGRATE", true); public static final AlertType ALERT_TYPE_USERVM_MIGRATE = new AlertType((short)17, "ALERT.USERVM.MIGRATE", true); public static final AlertType ALERT_TYPE_VLAN = new AlertType((short)18, "ALERT.NETWORK.VLAN", true); - public static final AlertType ALERT_TYPE_SSVM = new AlertType((short)19, "ALERT.SERVICE.SSVM", true); + public static final AlertType ALERT_TYPE_SSVM = new AlertType((short)19, "ALERT.SERVICE.SSVM", true, true); public static final AlertType ALERT_TYPE_USAGE_SERVER_RESULT = new AlertType((short)20, "ALERT.USAGE.RESULT", true); public static final AlertType ALERT_TYPE_STORAGE_DELETE = new AlertType((short)21, "ALERT.STORAGE.DELETE", true); public static final AlertType ALERT_TYPE_UPDATE_RESOURCE_COUNT = new AlertType((short)22, "ALERT.RESOURCE.COUNT", true); public static final AlertType ALERT_TYPE_USAGE_SANITY_RESULT = new AlertType((short)23, "ALERT.USAGE.SANITY", true); public static final AlertType ALERT_TYPE_DIRECT_ATTACHED_PUBLIC_IP = new AlertType((short)24, "ALERT.NETWORK.DIRECTPUBLICIP", true); public static final AlertType ALERT_TYPE_LOCAL_STORAGE = new AlertType((short)25, "ALERT.STORAGE.LOCAL", true); - public static final AlertType ALERT_TYPE_RESOURCE_LIMIT_EXCEEDED = new AlertType((short)26, "ALERT.RESOURCE.EXCEED", true); + public static final AlertType ALERT_TYPE_RESOURCE_LIMIT_EXCEEDED = new AlertType((short)26, "ALERT.RESOURCE.EXCEED", true, true); public static final AlertType ALERT_TYPE_SYNC = new AlertType((short)27, "ALERT.TYPE.SYNC", true); - public static final AlertType ALERT_TYPE_UPLOAD_FAILED = new AlertType((short)28, "ALERT.UPLOAD.FAILED", true); - public static final AlertType ALERT_TYPE_OOBM_AUTH_ERROR = new AlertType((short)29, "ALERT.OOBM.AUTHERROR", true); - public static final AlertType ALERT_TYPE_HA_ACTION = new AlertType((short)30, "ALERT.HA.ACTION", true); - public static final AlertType ALERT_TYPE_CA_CERT = new AlertType((short)31, "ALERT.CA.CERT", true); + public static final AlertType ALERT_TYPE_UPLOAD_FAILED = new AlertType((short)28, "ALERT.UPLOAD.FAILED", true, true); + public static final AlertType ALERT_TYPE_OOBM_AUTH_ERROR = new AlertType((short)29, "ALERT.OOBM.AUTHERROR", true, true); + public static final AlertType ALERT_TYPE_HA_ACTION = new AlertType((short)30, "ALERT.HA.ACTION", true, true); + public static final AlertType ALERT_TYPE_CA_CERT = new AlertType((short)31, "ALERT.CA.CERT", true, true); 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_VR_PUBLIC_IFACE_MTU = new AlertType((short)33, "ALERT.VR.PUBLIC.IFACE.MTU", true); + public static final AlertType ALERT_TYPE_VR_PRIVATE_IFACE_MTU = new AlertType((short)34, "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, true); + public static final AlertType ALERT_TYPE_VPN_GATEWAY_OBSOLETE_PARAMETERS = new AlertType((short)34, "ALERT.S2S.VPN.GATEWAY.OBSOLETE.PARAMETERS", true, 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 static final AlertType ALERT_TYPE_BACKUP_VALIDATION_FAILED = new AlertType((short)35, "ALERT.BACKUP.VALIDATION.FAILED", true, true); + public static final AlertType ALERT_TYPE_BACKUP_VALIDATION_UNABLE_TO_VALIDATE = new AlertType((short)36, "ALERT.BACKUP.VALIDATION.UNABLE.TO.VALIDATE", true, true); + public static final AlertType ALERT_TYPE_BACKUP_VALIDATION_CLEANUP_FAILED = new AlertType((short)37, "ALERT.BACKUP.VALIDATION.CLEANUP_FAILED", true, true); public short getType() { return type; @@ -85,6 +95,10 @@ public String getName() { return name; } + public boolean isRepetitionAllowed() { + return repetitionAllowed; + } + private static AlertType getAlertType(short type) { for (AlertType alertType : defaultAlertTypes) { if (alertType.getType() == type) { @@ -108,7 +122,7 @@ public static AlertType generateAlert(short type, String name) { if (defaultAlert != null && !defaultAlert.getName().equalsIgnoreCase(name)) { throw new InvalidParameterValueException("There is a default alert having type " + type + " and name " + defaultAlert.getName()); } else { - return new AlertType(type, name, false); + return new AlertType(type, name, false, false); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/APICommand.java b/api/src/main/java/org/apache/cloudstack/api/APICommand.java index c559be081165..b77649046ca9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/APICommand.java +++ b/api/src/main/java/org/apache/cloudstack/api/APICommand.java @@ -50,4 +50,6 @@ RoleType[] authorized() default {}; Class[] entityType() default {}; + + String httpMethod() default ""; } diff --git a/api/src/main/java/org/apache/cloudstack/api/AbstractGetUploadParamsCmd.java b/api/src/main/java/org/apache/cloudstack/api/AbstractGetUploadParamsCmd.java index 083a1be00f56..13f351f3a27f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/AbstractGetUploadParamsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/AbstractGetUploadParamsCmd.java @@ -29,28 +29,28 @@ public abstract class AbstractGetUploadParamsCmd extends BaseCmd { - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the volume/template/iso") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "The name of the Volume/Template/ISO") private String name; - @Parameter(name = ApiConstants.FORMAT, type = CommandType.STRING, required = true, description = "the format for the volume/template/iso. Possible values include QCOW2, OVA, " + @Parameter(name = ApiConstants.FORMAT, type = CommandType.STRING, required = true, description = "The format for the Volume/Template/ISO. Possible values include QCOW2, OVA, " + "and VHD.") private String format; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "the ID of the zone the volume/template/iso is " + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "The ID of the zone the Volume/Template/ISO is " + "to be hosted on") private Long zoneId; - @Parameter(name = ApiConstants.CHECKSUM, type = CommandType.STRING, description = "the checksum value of this volume/template/iso " + ApiConstants.CHECKSUM_PARAMETER_PREFIX_DESCRIPTION) + @Parameter(name = ApiConstants.CHECKSUM, type = CommandType.STRING, description = "The checksum value of this Volume/Template/ISO " + ApiConstants.CHECKSUM_PARAMETER_PREFIX_DESCRIPTION) private String checksum; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional accountName. Must be used with domainId.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "An optional accountName. Must be used with domainId.") private String accountName; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "an optional domainId. If the account parameter is used, " + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "An optional domainId. 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 = "Upload volume/template/iso for the project") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Upload Volume/Template/ISO for the project") private Long projectId; public String getName() { @@ -81,6 +81,34 @@ public Long getProjectId() { return projectId; } + public void setName(String name) { + this.name = name; + } + + public void setFormat(String format) { + this.format = format; + } + + public void setZoneId(Long zoneId) { + this.zoneId = zoneId; + } + + public void setChecksum(String checksum) { + this.checksum = checksum; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public void setDomainId(Long domainId) { + this.domainId = domainId; + } + + public void setProjectId(Long projectId) { + this.projectId = projectId; + } + public GetUploadParamsResponse createGetUploadParamsResponse(UUID id, URL postURL, String metadata, String timeout, String signature) { return new GetUploadParamsResponse(id, postURL, metadata, timeout, signature); } 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 4d33ba859a5b..2aa97b65a3d5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiCommandResourceType.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiCommandResourceType.java @@ -89,7 +89,9 @@ public enum ApiCommandResourceType { KubernetesSupportedVersion(null), SharedFS(org.apache.cloudstack.storage.sharedfs.SharedFS.class), Extension(org.apache.cloudstack.extension.Extension.class), - ExtensionCustomAction(org.apache.cloudstack.extension.ExtensionCustomAction.class); + ExtensionCustomAction(org.apache.cloudstack.extension.ExtensionCustomAction.class), + KmsKey(org.apache.cloudstack.kms.KMSKey.class), + HsmProfile(org.apache.cloudstack.kms.HSMProfile.class); private final Class clazz; @@ -127,8 +129,8 @@ public String toString() { } public static ApiCommandResourceType fromString(String value) { - if (StringUtils.isNotEmpty(value) && EnumUtils.isValidEnum(ApiCommandResourceType.class, value)) { - return valueOf(value); + if (StringUtils.isNotBlank(value) && EnumUtils.isValidEnumIgnoreCase(ApiCommandResourceType.class, value)) { + return EnumUtils.getEnumIgnoreCase(ApiCommandResourceType.class, value); } return null; } 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 489d737b5bb9..ac6acdf42516 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -19,6 +19,8 @@ public class ApiConstants { public static final String ACCOUNT = "account"; public static final String ACCOUNTS = "accounts"; + public static final String ACCOUNT_NAME = "accountname"; + public static final String ACCOUNT_STATE_TO_SHOW = "accountstatetoshow"; public static final String ACCOUNT_TYPE = "accounttype"; public static final String ACCOUNT_ID = "accountid"; public static final String ACCOUNT_IDS = "accountids"; @@ -27,6 +29,7 @@ public class ApiConstants { public static final String ACTIVATION_RULE = "activationrule"; public static final String ACTIVITY = "activity"; public static final String ADAPTER_TYPE = "adaptertype"; + public static final String ADDITONAL_CONFIG_ENABLED = "additionalconfigenabled"; public static final String ADDRESS = "address"; public static final String ALGORITHM = "algorithm"; public static final String ALIAS = "alias"; @@ -45,6 +48,7 @@ public class ApiConstants { public static final String AS_NUMBER_ID = "asnumberid"; public static final String ASN_RANGE = "asnrange"; public static final String ASN_RANGE_ID = "asnrangeid"; + public static final String API_KEY_FILTER = "apikeyfilter"; public static final String ASYNC_BACKUP = "asyncbackup"; public static final String AUTO_SELECT = "autoselect"; public static final String USER_API_KEY = "userapikey"; @@ -59,11 +63,15 @@ public class ApiConstants { public static final String BACKUP_LIMIT = "backuplimit"; public static final String BACKUP_OFFERING_NAME = "backupofferingname"; public static final String BACKUP_OFFERING_ID = "backupofferingid"; + public static final String BACKUP_OFFERING_DETAILS = "backupofferingdetails"; 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 IS_BACKUP_VM_EXPUNGED = "isbackupvmexpunged"; public static final String BACKUP_TOTAL = "backuptotal"; + public static final String BALANCE = "balance"; + public static final String BALANCES = "balances"; public static final String BASE64_IMAGE = "base64image"; public static final String BGP_PEERS = "bgppeers"; public static final String BGP_PEER_IDS = "bgppeerids"; @@ -72,6 +80,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 BLANK_INSTANCE = "blankinstance"; public static final String BUS_ADDRESS = "busaddress"; public static final String BYTES_READ_RATE = "bytesreadrate"; public static final String BYTES_READ_RATE_MAX = "bytesreadratemax"; @@ -80,6 +89,7 @@ public class ApiConstants { public static final String BYTES_WRITE_RATE_MAX = "byteswriteratemax"; public static final String BYTES_WRITE_RATE_MAX_LENGTH = "byteswriteratemaxlength"; public static final String BYPASS_VLAN_OVERLAP_CHECK = "bypassvlanoverlapcheck"; + public static final String CALLER = "caller"; public static final String CAPACITY = "capacity"; public static final String CATEGORY = "category"; public static final String CAN_REVERT = "canrevert"; @@ -132,6 +142,7 @@ public class ApiConstants { 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 CSI_ENABLED = "csienabled"; public static final String COMPONENT = "component"; public static final String CPU = "CPU"; public static final String CPU_CORE_PER_SOCKET = "cpucorepersocket"; @@ -139,6 +150,7 @@ public class ApiConstants { public static final String CPU_SPEED = "cpuspeed"; public static final String CPU_LOAD_AVERAGE = "cpuloadaverage"; public static final String CREATED = "created"; + public static final String CROSS_ZONE_INSTANCE_CREATION = "crosszoneinstancecreation"; public static final String CTX_ACCOUNT_ID = "ctxaccountid"; public static final String CTX_DETAILS = "ctxDetails"; public static final String CTX_USER_ID = "ctxuserid"; @@ -149,6 +161,7 @@ public class ApiConstants { 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 CURRENCY = "currency"; public static final String CURRENT_START_IP = "currentstartip"; public static final String CURRENT_END_IP = "currentendip"; public static final String ENCRYPT = "encrypt"; @@ -162,6 +175,7 @@ public class ApiConstants { 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 DATE = "date"; public static final String DEFAULT_VALUE = "defaultvalue"; public static final String DELETE_PROTECTION = "deleteprotection"; public static final String DESCRIPTION = "description"; @@ -189,6 +203,7 @@ public class ApiConstants { public static final String UTILIZATION = "utilization"; public static final String DRIVER = "driver"; public static final String ROOT_DISK_SIZE = "rootdisksize"; + public static final String ROOT_DISK_KMS_KEY_ID = "rootdiskkmskeyid"; public static final String DHCP_OPTIONS_NETWORK_LIST = "dhcpoptionsnetworklist"; public static final String DHCP_OPTIONS = "dhcpoptions"; public static final String DHCP_PREFIX = "dhcp:"; @@ -208,9 +223,11 @@ public class ApiConstants { public static final String DOMAIN_PATH = "domainpath"; public static final String DOMAIN_ID = "domainid"; public static final String DOMAIN__ID = "domainId"; + public static final String DUMMY = "dummy"; public static final String DURATION = "duration"; public static final String ELIGIBLE = "eligible"; public static final String EMAIL = "email"; + public static final String ENABLE_CSI = "enablecsi"; public static final String END_ASN = "endasn"; public static final String END_DATE = "enddate"; public static final String END_IP = "endip"; @@ -222,6 +239,7 @@ public class ApiConstants { public static final String EVENT_TYPE = "eventtype"; public static final String EXPIRES = "expires"; public static final String EXTRA_CONFIG = "extraconfig"; + public static final String EXTRA_PARAMS = "extraparams"; public static final String EXTRA_DHCP_OPTION = "extradhcpoption"; public static final String EXTRA_DHCP_OPTION_NAME = "extradhcpoptionname"; public static final String EXTRA_DHCP_OPTION_CODE = "extradhcpoptioncode"; @@ -240,6 +258,8 @@ public class ApiConstants { public static final String FIRSTNAME = "firstname"; public static final String FORCED = "forced"; public static final String FORCED_DESTROY_LOCAL_STORAGE = "forcedestroylocalstorage"; + public static final String FORCE_CONVERT_TO_POOL = "forceconverttopool"; + 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"; @@ -247,6 +267,7 @@ public class ApiConstants { 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 FROM_CHECKPOINT_ID = "fromcheckpointid"; public static final String FULL_PATH = "fullpath"; public static final String GATEWAY = "gateway"; public static final String IP6_GATEWAY = "ip6gateway"; @@ -266,6 +287,7 @@ public class ApiConstants { public static final String HEALTH = "health"; public static final String HEADERS = "headers"; public static final String HIDE_IP_ADDRESS_USAGE = "hideipaddressusage"; + public static final String HISTORY = "history"; public static final String HOST_ID = "hostid"; public static final String HOST_IDS = "hostids"; public static final String HOST_IP = "hostip"; @@ -273,6 +295,7 @@ public class ApiConstants { public static final String HOST = "host"; public static final String HOST_CONTROL_STATE = "hostcontrolstate"; public static final String HOSTS_MAP = "hostsmap"; + public static final String HTTP_REQUEST_TYPE = "httprequesttype"; public static final String HYPERVISOR = "hypervisor"; public static final String INLINE = "inline"; public static final String INSTANCE = "instance"; @@ -318,6 +341,7 @@ public class ApiConstants { public static final String IS_2FA_VERIFIED = "is2faverified"; public static final String IS_2FA_MANDATED = "is2famandated"; + public static final String IS_ACTIVE = "isactive"; public static final String IS_ASYNC = "isasync"; public static final String IP_AVAILABLE = "ipavailable"; public static final String IP_LIMIT = "iplimit"; @@ -346,6 +370,7 @@ public class ApiConstants { public static final String JOB_STATUS = "jobstatus"; public static final String KEEPALIVE_ENABLED = "keepaliveenabled"; public static final String KERNEL_VERSION = "kernelversion"; + public static final String KEYPAIR_ID = "keypairid"; public static final String KEY = "key"; public static final String LABEL = "label"; public static final String LASTNAME = "lastname"; @@ -366,6 +391,7 @@ public class ApiConstants { 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 MATCH_TYPE = "matchtype"; public static final String MAX = "max"; public static final String MAX_SNAPS = "maxsnaps"; public static final String MAX_BACKUPS = "maxbackups"; @@ -426,6 +452,7 @@ public class ApiConstants { 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 GUEST_OS_RULE = "guestosrule"; public static final String OS_MAPPING_CHECK_ENABLED = "osmappingcheckenabled"; public static final String OUTOFBANDMANAGEMENT_POWERSTATE = "outofbandmanagementpowerstate"; public static final String OUTOFBANDMANAGEMENT_ENABLED = "outofbandmanagementenabled"; @@ -492,7 +519,9 @@ public class ApiConstants { public static final String RECONNECT = "reconnect"; public static final String RECOVER = "recover"; public static final String REPAIR = "repair"; + public static final String REPETITION_ALLOWED = "repetitionallowed"; public static final String REQUIRES_HVM = "requireshvm"; + public static final String RESERVED_RESOURCE_DETAILS = "reservedresourcedetails"; public static final String RESOURCES = "resources"; public static final String RESOURCE_COUNT = "resourcecount"; public static final String RESOURCE_NAME = "resourcename"; @@ -506,12 +535,12 @@ public class ApiConstants { public static final String QUALIFIERS = "qualifiers"; public static final String QUERY_FILTER = "queryfilter"; public static final String QUIESCE_VM = "quiescevm"; + public static final String QUICK_RESTORE = "quickrestore"; public static final String SCHEDULE = "schedule"; public static final String SCHEDULE_ID = "scheduleid"; public static final String SCOPE = "scope"; public static final String SEARCH_BASE = "searchbase"; public static final String SECONDARY_IP = "secondaryip"; - public static final String SECRET_KEY = "secretkey"; public static final String SECURITY_GROUP_IDS = "securitygroupids"; public static final String SECURITY_GROUP_NAMES = "securitygroupnames"; public static final String SECURITY_GROUP_NAME = "securitygroupname"; @@ -525,9 +554,11 @@ public class ApiConstants { public static final String SESSIONKEY = "sessionkey"; public static final String SHOW_CAPACITIES = "showcapacities"; public static final String SHOW_REMOVED = "showremoved"; + public static final String SHOW_RESOURCES = "showresources"; public static final String SHOW_RESOURCE_ICON = "showicon"; public static final String SHOW_INACTIVE = "showinactive"; public static final String SHOW_UNIQUE = "showunique"; + public static final String SHOW_PERMISSIONS = "showpermissions"; public static final String SIGNATURE = "signature"; public static final String SIGNATURE_VERSION = "signatureversion"; public static final String SINCE = "since"; @@ -543,6 +574,7 @@ public class ApiConstants { public static final String USE_STORAGE_REPLICATION = "usestoragereplication"; public static final String SOURCE_CIDR_LIST = "sourcecidrlist"; + public static final String SOURCE_OFFERING_ID = "sourceofferingid"; public static final String SOURCE_ZONE_ID = "sourcezoneid"; public static final String SSL_VERIFICATION = "sslverification"; public static final String START_ASN = "startasn"; @@ -554,6 +586,8 @@ 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 COMPRESSION_STATUS = "compressionstatus"; + public static final String VALIDATION_STATUS = "validationstatus"; public static final String STORAGE_TYPE = "storagetype"; public static final String STORAGE_POLICY = "storagepolicy"; public static final String STORAGE_MOTION_ENABLED = "storagemotionenabled"; @@ -575,6 +609,9 @@ public class ApiConstants { public static final String SUITABLE_FOR_VM = "suitableforvirtualmachine"; public static final String SUPPORTS_STORAGE_SNAPSHOT = "supportsstoragesnapshot"; public static final String TARGET_IQN = "targetiqn"; + public static final String TARIFF_ID = "tariffid"; + public static final String TARIFF_NAME = "tariffname"; + public static final String TASKS_FILTER = "tasksfilter"; public static final String TEMPLATE_FILTER = "templatefilter"; public static final String TEMPLATE_ID = "templateid"; public static final String TEMPLATE_IDS = "templateids"; @@ -587,9 +624,12 @@ public class ApiConstants { public static final String TENANT_NAME = "tenantname"; public static final String TOTAL = "total"; public static final String TOTAL_SUBNETS = "totalsubnets"; + public static final String TO_CHECKPOINT_ID = "tocheckpointid"; + public static final String TOTAL_QUOTA = "totalquota"; public static final String TYPE = "type"; public static final String TRUST_STORE = "truststore"; public static final String TRUST_STORE_PASSWORD = "truststorepass"; + public static final String UNIT = "unit"; public static final String URL = "url"; public static final String USAGE_INTERFACE = "usageinterface"; public static final String USED = "used"; @@ -611,6 +651,7 @@ public class ApiConstants { public static final String USER_CONFIGURABLE = "userconfigurable"; public static final String USER_SECURITY_GROUP_LIST = "usersecuritygrouplist"; public static final String USER_SECRET_KEY = "usersecretkey"; + public static final String USE_VDDK = "usevddk"; 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"; @@ -631,6 +672,7 @@ public class ApiConstants { public static final String VIRTUAL_MACHINE_STATE = "vmstate"; public static final String VIRTUAL_MACHINES = "virtualmachines"; public static final String USAGE_ID = "usageid"; + public static final String USAGE_NAME = "usagename"; public static final String USAGE_TYPE = "usagetype"; public static final String INCLUDE_TAGS = "includetags"; @@ -644,6 +686,7 @@ public class ApiConstants { 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 = "isolated"; public static final String ISOLATED_PVLAN = "isolatedpvlan"; public static final String ISOLATED_PVLAN_TYPE = "isolatedpvlantype"; public static final String ISOLATION_URI = "isolationuri"; @@ -752,6 +795,7 @@ public class ApiConstants { public static final String ROLE_TYPE = "roletype"; public static final String ROLE_NAME = "rolename"; public static final String PERMISSION = "permission"; + public static final String PERMISSIONS = "permissions"; public static final String RULE = "rule"; public static final String RULES = "rules"; public static final String RULE_ID = "ruleid"; @@ -846,9 +890,17 @@ public class ApiConstants { public static final String IS_SOURCE_NAT = "issourcenat"; public static final String IS_STATIC_NAT = "isstaticnat"; public static final String ITERATIONS = "iterations"; + public static final String ITEMS = "items"; public static final String SORT_BY = "sortby"; public static final String CHANGE_CIDR = "changecidr"; + public static final String HSM_PROFILE = "hsmprofile"; + public static final String HSM_PROFILE_ID = "hsmprofileid"; public static final String PURPOSE = "purpose"; + public static final String KMS_KEY = "kmskey"; + public static final String KMS_KEY_ID = "kmskeyid"; + public static final String KMS_KEY_VERSION = "kmskeyversion"; + public static final String KEK_LABEL = "keklabel"; + public static final String KEY_BITS = "keybits"; public static final String IS_TAGGED = "istagged"; public static final String INSTANCE_NAME = "instancename"; public static final String CONSIDER_LAST_HOST = "considerlasthost"; @@ -969,6 +1021,7 @@ public class ApiConstants { public static final String REGION_ID = "regionid"; public static final String VPC_OFF_ID = "vpcofferingid"; public static final String VPC_OFF_NAME = "vpcofferingname"; + public static final String VPC_OFFERING_CONSERVE_MODE = "vpcofferingconservemode"; public static final String NETWORK = "network"; public static final String VPC_ID = "vpcid"; public static final String VPC_NAME = "vpcname"; @@ -1015,7 +1068,7 @@ public class ApiConstants { public static final String NSX_PROVIDER_PORT = "nsxproviderport"; public static final String NSX_CONTROLLER_ID = "nsxcontrollerid"; public static final String S3_ACCESS_KEY = "accesskey"; - public static final String S3_SECRET_KEY = "secretkey"; + public static final String SECRET_KEY = "secretkey"; public static final String S3_END_POINT = "endpoint"; public static final String S3_BUCKET_NAME = "bucket"; public static final String S3_SIGNER = "s3signer"; @@ -1154,8 +1207,11 @@ public class ApiConstants { 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_EXTERNAL_DETAILS = "cleanupexternaldetails"; + public static final String CLEAN_UP_EXTRA_CONFIG = "cleanupextraconfig"; public static final String CLEAN_UP_PARAMETERS = "cleanupparameters"; public static final String VIRTUAL_SIZE = "virtualsize"; + public static final String UNCOMPRESSED_SIZE = "uncompressedsize"; public static final String NETSCALER_CONTROLCENTER_ID = "netscalercontrolcenterid"; public static final String NETSCALER_SERVICEPACKAGE_ID = "netscalerservicepackageid"; public static final String FETCH_ROUTER_HEALTH_CHECK_RESULTS = "fetchhealthcheckresults"; @@ -1204,6 +1260,7 @@ public class ApiConstants { public static final String DOCKER_REGISTRY_EMAIL = "dockerregistryemail"; public static final String ISO_NAME = "isoname"; public static final String ISO_STATE = "isostate"; + public static final String ISO_URL = "isourl"; public static final String SEMANTIC_VERSION = "semanticversion"; public static final String KUBERNETES_VERSION_ID = "kubernetesversionid"; public static final String KUBERNETES_VERSION_NAME = "kubernetesversionname"; @@ -1224,6 +1281,13 @@ public class ApiConstants { 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 NODE_TYPE_AFFINITY_GROUP_MAP = "nodeaffinitygroups"; + public static final String CONTROL_AFFINITY_GROUP_IDS = "controlaffinitygroupids"; + public static final String CONTROL_AFFINITY_GROUP_NAMES = "controlaffinitygroupnames"; + public static final String WORKER_AFFINITY_GROUP_IDS = "workeraffinitygroupids"; + public static final String WORKER_AFFINITY_GROUP_NAMES = "workeraffinitygroupnames"; + public static final String ETCD_AFFINITY_GROUP_IDS = "etcdaffinitygroupids"; + public static final String ETCD_AFFINITY_GROUP_NAMES = "etcdaffinitygroupnames"; public static final String BOOT_TYPE = "boottype"; public static final String BOOT_MODE = "bootmode"; @@ -1245,6 +1309,7 @@ public class ApiConstants { public static final String PROVIDER_FOR_2FA = "providerfor2fa"; public static final String ISSUER_FOR_2FA = "issuerfor2fa"; public static final String MANDATE_2FA = "mandate2fa"; + public static final String PASSWORD_CHANGE_REQUIRED = "passwordchangerequired"; public static final String SECRET_CODE = "secretcode"; public static final String LOGIN = "login"; public static final String LOGOUT = "logout"; @@ -1267,6 +1332,8 @@ public class ApiConstants { public static final String OBJECT_LOCKING = "objectlocking"; public static final String ENCRYPTION = "encryption"; public static final String QUOTA = "quota"; + public static final String QUOTA_CONSUMED = "quotaconsumed"; + public static final String QUOTA_USAGE = "quotausage"; public static final String ACCESS_KEY = "accesskey"; public static final String SOURCE_NAT_IP = "sourcenatipaddress"; @@ -1281,7 +1348,7 @@ public class ApiConstants { public static final String IMPORT_SOURCE = "importsource"; public static final String TEMP_PATH = "temppath"; public static final String HEURISTIC_RULE = "heuristicrule"; - public static final String HEURISTIC_TYPE_VALID_OPTIONS = "Valid options are: ISO, SNAPSHOT, TEMPLATE and VOLUME."; + public static final String HEURISTIC_TYPE_VALID_OPTIONS = "Valid options are: ISO, SNAPSHOT, BACKUP, TEMPLATE and VOLUME."; public static final String MANAGEMENT = "management"; public static final String IS_VNF = "isvnf"; public static final String VNF_NICS = "vnfnics"; @@ -1291,8 +1358,10 @@ public class ApiConstants { public static final String VNF_CONFIGURE_MANAGEMENT = "vnfconfiguremanagement"; public static final String VNF_CIDR_LIST = "vnfcidrlist"; + public static final String AUTHORIZE_URL = "authorizeurl"; public static final String CLIENT_ID = "clientid"; public static final String REDIRECT_URI = "redirecturi"; + public static final String TOKEN_URL = "tokenurl"; public static final String IS_TAG_A_RULE = "istagarule"; @@ -1317,6 +1386,45 @@ public class ApiConstants { public static final String OBJECT_STORAGE_LIMIT = "objectstoragelimit"; public static final String OBJECT_STORAGE_TOTAL = "objectstoragetotal"; + public static final String KEEP_MAC_ADDRESS_ON_PUBLIC_NIC = "keepmacaddressonpublicnic"; + + public static final String PARAMETER_DESCRIPTION_KEEP_MAC_ADDRESS_ON_PUBLIC_NIC = + "Indicates whether to use the same MAC address for the public NIC of VRs on the same network. If \"true\", when creating redundant routers or recreating" + + " a VR, CloudStack will use the same MAC address for the public NIC of all VRs. Otherwise, if \"false\", new public NICs will always have " + + " a new MAC address."; + + // DNS provider related + public static final String NAME_SERVERS = "nameservers"; + public static final String DNS_USER_NAME = "dnsusername"; + public static final String DNS_API_KEY = "dnsapikey"; + public static final String DNS_ZONE_ID = "dnszoneid"; + public static final String DNS_ZONE = "dnszone"; + public static final String DNS_RECORD = "dnsrecord"; + public static final String DNS_SUB_DOMAIN = "dnssubdomain"; + public static final String DNS_SERVER_ID = "dnsserverid"; + public static final String CONTENT = "content"; + public static final String CONTENTS = "contents"; + public static final String PUBLIC_DOMAIN_SUFFIX = "publicdomainsuffix"; + public static final String AUTHORITATIVE = "authoritative"; + public static final String KIND = "kind"; + public static final String DNS_SEC = "dnssec"; + public static final String TTL = "ttl"; + public static final String CHANGE_TYPE = "changetype"; + public static final String RECORDS = "records"; + public static final String RR_SETS = "rrsets"; + public static final String X_API_KEY = "X-API-Key"; + public static final String DISABLED = "disabled"; + public static final String CONTENT_TYPE = "Content-Type"; + public static final String NATIVE_ZONE = "Native"; + public static final String NIC_DNS_NAME = "nicdnsname"; + public static final String TIME_STAMP = "timestamp"; + public static final String INSTANCE_ID = "instanceId"; + public static final String OLD_STATE = "oldState"; + public static final String NEW_STATE = "newState"; + public static final String OLD_HOST_NAME = "oldHostName"; + public static final String EXISTING = "existing"; + public static final String UNMANAGE = "unmanage"; + public static final String PARAMETER_DESCRIPTION_ACTIVATION_RULE = "Quota tariff's activation rule. It can receive a JS script that results in either " + "a boolean or a numeric value: if it results in a boolean value, the tariff value will be applied according to the result; if it results in a numeric value, the " + "numeric value will be applied; if the result is neither a boolean nor a numeric value, the tariff will not be applied. If the rule is not informed, the tariff " + @@ -1336,9 +1444,14 @@ public class ApiConstants { public static final String VMWARE_DC = "vmwaredc"; + public static final String PARAMETER_DESCRIPTION_ISOLATED_BACKUPS = "Whether the backup will be isolated, defaults to false. " + + "Isolated backups are always created as full backups in independent chains. Therefore, they will never depend on any existing backup chain " + + "and no backup chain will depend on them. Currently only supported for the KBOSS provider."; + public static final String CSS = "css"; public static final String JSON_CONFIGURATION = "jsonconfiguration"; + public static final String LOGIN_BASE_DOMAIN = "loginbasedomain"; public static final String COMMON_NAMES = "commonnames"; @@ -1352,6 +1465,31 @@ public class ApiConstants { public static final String RECURSIVE_DOMAINS = "recursivedomains"; + public static final String VPN_CUSTOMER_GATEWAY_PARAMETERS = "vpncustomergatewayparameters"; + public static final String OBSOLETE_PARAMETERS = "obsoleteparameters"; + public static final String EXCLUDED_PARAMETERS = "excludedparameters"; + + public static final String COMPRESS = "compress"; + + public static final String VALIDATE = "validate"; + + public static final String VALIDATION_STEPS = "validationsteps"; + + public static final String ALLOW_QUICK_RESTORE = "allowquickrestore"; + + public static final String ALLOW_EXTRACT_FILE = "allowextractfile"; + + public static final String BACKUP_CHAIN_SIZE = "backupchainsize"; + + public static final String COMPRESSION_LIBRARY = "compressionlibrary"; + public static final String ATTEMPTS = "attempts"; + + public static final String EXECUTING = "executing"; + + public static final String SCHEDULED = "scheduled"; + public static final String SCHEDULED_DATE = "scheduleddate"; + public static final String BACKUP_PROVIDER = "backupprovider"; + /** * 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). @@ -1393,7 +1531,7 @@ public String toString() { } public enum HostDetails { - all, capacity, events, stats, min; + all, capacity, core, events, stats, min; } public enum VMDetails { 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 cb75939d6bc5..1ee41ac86c22 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiServerService.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiServerService.java @@ -21,8 +21,11 @@ import javax.servlet.http.HttpSession; +import org.apache.cloudstack.context.CallContext; + import com.cloud.domain.Domain; import com.cloud.exception.CloudAuthenticationException; +import com.cloud.user.Account; import com.cloud.user.UserAccount; public interface ApiServerService { @@ -49,5 +52,23 @@ public ResponseObject loginUser(HttpSession session, String username, String pas boolean resetPassword(UserAccount userAccount, String token, String password); + String getDomainId(Map params); + boolean isPostRequestsAndTimestampsEnforced(); + + AsyncCmdResult processAsyncCmd(BaseAsyncCmd cmdObj, Map params, CallContext ctx, Long callerUserId, Account caller) throws Exception; + + class AsyncCmdResult { + public final Long objectId; + public final String objectUuid; + public final BaseAsyncCmd asyncCmd; + public final long jobId; + + public AsyncCmdResult(Long objectId, String objectUuid, BaseAsyncCmd asyncCmd, long jobId) { + this.objectId = objectId; + this.objectUuid = objectUuid; + this.asyncCmd = asyncCmd; + this.jobId = jobId; + } + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseAsyncCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseAsyncCmd.java index 6859b0a7f406..c67c5a023e09 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseAsyncCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseAsyncCmd.java @@ -29,6 +29,7 @@ public abstract class BaseAsyncCmd extends BaseCmd { public static final String migrationSyncObject = "migration"; public static final String snapshotHostSyncObject = "snapshothost"; public static final String gslbSyncObject = "globalserverloadbalancer"; + public static final String user = "user"; private Object job; diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseAsyncCreateCustomIdCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseAsyncCreateCustomIdCmd.java index 8680d7f11de3..b9b13dcfd884 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseAsyncCreateCustomIdCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseAsyncCreateCustomIdCmd.java @@ -19,8 +19,8 @@ public abstract class BaseAsyncCreateCustomIdCmd extends BaseAsyncCreateCmd { @Parameter(name = ApiConstants.CUSTOM_ID, type = CommandType.STRING, - description = "an optional field, in case you want to set a custom id to the resource. Allowed to Root Admins only") - private String customId; + description = "An optional field, in case you want to set a custom id to the resource. Allowed to Root Admins only") + protected String customId; public String getCustomId() { return customId; diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseAsyncCustomIdCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseAsyncCustomIdCmd.java index b251c6ef2ec2..c1777d3b8f84 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseAsyncCustomIdCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseAsyncCustomIdCmd.java @@ -21,7 +21,7 @@ public abstract class BaseAsyncCustomIdCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.CUSTOM_ID, type = CommandType.STRING, - description = "an optional field, in case you want to set a custom id to the resource. Allowed to Root Admins only", since = "4.4", authorized = {RoleType.Admin}) + description = "An optional field, in case you want to set a custom id to the resource. Allowed to Root Admins only", since = "4.4", authorized = {RoleType.Admin}) private String customId; public String getCustomId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseBackupListCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseBackupListCmd.java index 0aa8366bcd5c..2a64a1fb6fd8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseBackupListCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseBackupListCmd.java @@ -25,7 +25,7 @@ import org.apache.cloudstack.backup.BackupOffering; import org.apache.cloudstack.context.CallContext; -public abstract class BaseBackupListCmd extends BaseListCmd { +public abstract class BaseBackupListCmd extends BaseListAccountResourcesCmd { protected void setupResponseBackupOfferingsList(final List offerings, final Integer count) { final ListResponse response = new ListResponse<>(); 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 8f47d51b19d4..483fa83630ad 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java @@ -35,10 +35,12 @@ import org.apache.cloudstack.acl.ProjectRoleService; import org.apache.cloudstack.acl.RoleService; import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.acl.apikeypair.ApiKeyPairService; 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.dns.DnsProviderManager; import org.apache.cloudstack.gpu.GpuService; import org.apache.cloudstack.network.RoutedIpv4Manager; import org.apache.cloudstack.network.lb.ApplicationLoadBalancerService; @@ -220,6 +222,8 @@ public static enum CommandType { @Inject public Ipv6Service ipv6Service; @Inject + public ApiKeyPairService apiKeyPairService; + @Inject public VnfTemplateManager vnfTemplateManager; @Inject public BucketApiService _bucketService; @@ -229,6 +233,9 @@ public static enum CommandType { @Inject public RoutedIpv4Manager routedIpv4Manager; + @Inject + public DnsProviderManager dnsProviderManager; + public abstract void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException; @@ -382,7 +389,7 @@ public List getParamFields() { if (roleIsAllowed) { validFields.add(field); } else { - logger.debug("Ignoring parameter " + parameterAnnotation.name() + " as the caller is not authorized to pass it in"); + logger.debug("Ignoring parameter {} as the caller is not authorized to pass it in", parameterAnnotation.name()); } } @@ -498,4 +505,8 @@ public Map convertExternalDetailsToMap(Map externalDetails) { } return details; } + + public String getResourceUuid(String parameterName) { + return CallContext.current().getApiResourceUuid(parameterName); + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseCustomIdCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseCustomIdCmd.java index 7ca9f1efe7ef..8a2292eadd07 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseCustomIdCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseCustomIdCmd.java @@ -22,7 +22,7 @@ public abstract class BaseCustomIdCmd extends BaseCmd { @Parameter(name = ApiConstants.CUSTOM_ID, type = CommandType.STRING, - description = "an optional field, in case you want to set a custom id to the resource. Allowed to Root Admins only", since = "4.4", authorized = {RoleType.Admin}) + description = "An optional field, in case you want to set a custom id to the resource. Allowed to Root Admins only", since = "4.4", authorized = {RoleType.Admin}) private String customId; public String getCustomId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseListAccountResourcesCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseListAccountResourcesCmd.java index aa5273ace3bc..bb18080dfcc4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseListAccountResourcesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseListAccountResourcesCmd.java @@ -19,7 +19,7 @@ public abstract class BaseListAccountResourcesCmd extends BaseListDomainResourcesCmd implements IBaseListAccountResourcesCmd { - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "list resources by account. Must be used with the domainId parameter.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "List resources by Account. Must be used with the domainId parameter.") private String accountName; @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseListDomainResourcesCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseListDomainResourcesCmd.java index 7a8cee337705..640caa935425 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseListDomainResourcesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseListDomainResourcesCmd.java @@ -27,10 +27,10 @@ public abstract class BaseListDomainResourcesCmd extends BaseListCmd implements @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "list only resources belonging to the domain specified") + description = "List only resources belonging to the domain specified") private Long domainId; - @Parameter(name = ApiConstants.IS_RECURSIVE, type = CommandType.BOOLEAN, description = "defaults to false," + @Parameter(name = ApiConstants.IS_RECURSIVE, type = CommandType.BOOLEAN, description = "Defaults to false," + " but if true, lists all resources from the parent specified by the domainId till leaves.") private Boolean recursive; diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseListProjectAndAccountResourcesCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseListProjectAndAccountResourcesCmd.java index 0bcfba15ea6e..d3c999ddb048 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseListProjectAndAccountResourcesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseListProjectAndAccountResourcesCmd.java @@ -20,7 +20,7 @@ public abstract class BaseListProjectAndAccountResourcesCmd extends BaseListAccountResourcesCmd implements IBaseListProjectAndAccountResourcesCmd { - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "list objects by project; if projectid=-1 lists All VMs") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "List objects by project; if projectid=-1 lists All Instances") private Long projectId; @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseListRetrieveOnlyResourceCountCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseListRetrieveOnlyResourceCountCmd.java index 0e8e136a6c19..20c270c30b21 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseListRetrieveOnlyResourceCountCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseListRetrieveOnlyResourceCountCmd.java @@ -19,7 +19,7 @@ import org.apache.commons.lang3.BooleanUtils; public abstract class BaseListRetrieveOnlyResourceCountCmd extends BaseListTaggedResourcesCmd { - @Parameter(name = ApiConstants.RETRIEVE_ONLY_RESOURCE_COUNT, type = CommandType.BOOLEAN, description = "makes the API's response contains only the resource count") + @Parameter(name = ApiConstants.RETRIEVE_ONLY_RESOURCE_COUNT, type = CommandType.BOOLEAN, description = "Makes the API's response contains only the resource count") private Boolean retrieveOnlyResourceCount; public Boolean getRetrieveOnlyResourceCount() { diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseListTemplateOrIsoPermissionsCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseListTemplateOrIsoPermissionsCmd.java index be95547a8a73..27e58233b249 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseListTemplateOrIsoPermissionsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseListTemplateOrIsoPermissionsCmd.java @@ -33,7 +33,7 @@ public abstract class BaseListTemplateOrIsoPermissionsCmd extends BaseCmd implem //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TemplatePermissionsResponse.class, required = true, description = "the template ID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TemplatePermissionsResponse.class, required = true, description = "The Template ID") private Long id; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseResponse.java b/api/src/main/java/org/apache/cloudstack/api/BaseResponse.java index 45016c1a2a26..ebf0c4b19a00 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseResponse.java @@ -25,11 +25,11 @@ public abstract class BaseResponse implements ResponseObject { private transient String objectName; @SerializedName(ApiConstants.JOB_ID) - @Param(description = "the UUID of the latest async job acting on this object") + @Param(description = "The UUID of the latest async job acting on this object") protected String jobId; @SerializedName(ApiConstants.JOB_STATUS) - @Param(description = "the current status of the latest async job acting on this object") + @Param(description = "The current status of the latest async job acting on this object") private Integer jobStatus; public BaseResponse() { diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseResponseWithAnnotations.java b/api/src/main/java/org/apache/cloudstack/api/BaseResponseWithAnnotations.java index f7c0c21395f8..19d88382ce78 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseResponseWithAnnotations.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseResponseWithAnnotations.java @@ -22,7 +22,7 @@ public abstract class BaseResponseWithAnnotations extends BaseResponse { @SerializedName(ApiConstants.HAS_ANNOTATIONS) - @Param(description = "true if the entity/resource has annotations") + @Param(description = "True if the entity/resource has annotations") private Boolean hasAnnotation; public Boolean hasAnnotation() { diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseResponseWithAssociatedNetwork.java b/api/src/main/java/org/apache/cloudstack/api/BaseResponseWithAssociatedNetwork.java index 1ffe4657bd98..48be5c6b4de0 100755 --- a/api/src/main/java/org/apache/cloudstack/api/BaseResponseWithAssociatedNetwork.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseResponseWithAssociatedNetwork.java @@ -22,11 +22,11 @@ public abstract class BaseResponseWithAssociatedNetwork extends BaseResponseWithAnnotations { @SerializedName(ApiConstants.ASSOCIATED_NETWORK_ID) - @Param(description = "the ID of the Network associated with this private gateway") + @Param(description = "The ID of the Network associated with this private gateway") private String associatedNetworkId; @SerializedName(ApiConstants.ASSOCIATED_NETWORK) - @Param(description = "the name of the Network associated with this private gateway") + @Param(description = "The name of the Network associated with this private gateway") private String associatedNetworkName; public void setAssociatedNetworkId(String associatedNetworkId) { diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseResponseWithTagInformation.java b/api/src/main/java/org/apache/cloudstack/api/BaseResponseWithTagInformation.java index 710b9f0b9ecf..a01cd5677bac 100755 --- a/api/src/main/java/org/apache/cloudstack/api/BaseResponseWithTagInformation.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseResponseWithTagInformation.java @@ -26,7 +26,7 @@ public abstract class BaseResponseWithTagInformation extends BaseResponseWithAnnotations { @SerializedName(ApiConstants.TAGS) - @Param(description = "the list of resource tags associated", responseObject = ResourceTagResponse.class) + @Param(description = "The list of resource tags associated", responseObject = ResourceTagResponse.class) protected Set tags; public void addTag(ResourceTagResponse tag) { 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 8489bf05ec78..94c5d8ff39fc 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoCmd.java @@ -30,49 +30,49 @@ public abstract class BaseUpdateTemplateOrIsoCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.BOOTABLE, type = CommandType.BOOLEAN, description = "true if image is bootable, false otherwise; available only for updateIso API") + @Parameter(name = ApiConstants.BOOTABLE, type = CommandType.BOOLEAN, description = "True if image is bootable, false otherwise; available only for updateIso API") private Boolean bootable; - @Parameter(name = ApiConstants.REQUIRES_HVM, type = CommandType.BOOLEAN, description = "true if the template requires HVM, false otherwise; available only for updateTemplate API") + @Parameter(name = ApiConstants.REQUIRES_HVM, type = CommandType.BOOLEAN, description = "True if the Template requires HVM, false otherwise; available only for updateTemplate API") private Boolean requiresHvm; - @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "the display text of the image", length = 4096) + @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "The display text of the image", length = 4096) private String displayText; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TemplateResponse.class, required = true, description = "the ID of the image file") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TemplateResponse.class, required = true, description = "The ID of the image file") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the image file") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, length = 251, description = "The name of the image file") private String templateName; @Parameter(name = ApiConstants.OS_TYPE_ID, type = CommandType.UUID, entityType = GuestOSResponse.class, - description = "the ID of the OS type that best represents the OS of this image.") + 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") + @Parameter(name = ApiConstants.FORMAT, type = CommandType.STRING, description = "The format for the image") private String format; - @Parameter(name = ApiConstants.PASSWORD_ENABLED, type = CommandType.BOOLEAN, description = "true if the image supports the password reset feature; default is false") + @Parameter(name = ApiConstants.PASSWORD_ENABLED, type = CommandType.BOOLEAN, description = "True if the image supports the password reset feature; default is false") private Boolean passwordEnabled; - @Parameter(name = ApiConstants.SSHKEY_ENABLED, type = CommandType.BOOLEAN, description = "true if the template supports the sshkey upload feature; default is false") + @Parameter(name = ApiConstants.SSHKEY_ENABLED, type = CommandType.BOOLEAN, description = "True if the Template supports the SSHkey upload feature; default is false") private Boolean sshKeyEnabled; - @Parameter(name = ApiConstants.SORT_KEY, type = CommandType.INTEGER, description = "sort key of the template, integer") + @Parameter(name = ApiConstants.SORT_KEY, type = CommandType.INTEGER, description = "Sort key of the Template, integer") private Integer sortKey; @Parameter(name = ApiConstants.IS_DYNAMICALLY_SCALABLE, type = CommandType.BOOLEAN, - description = "true if template/ISO contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory") + description = "True if Template/ISO contains XS/VMWare tools in order to support dynamic scaling of Instance CPU/memory") private Boolean isDynamicallyScalable; - @Parameter(name = ApiConstants.ROUTING, type = CommandType.BOOLEAN, description = "true if the template type is routing i.e., if template is used to deploy router") + @Parameter(name = ApiConstants.ROUTING, type = CommandType.BOOLEAN, description = "True if the Template type is routing i.e., if Template is used to deploy router") protected Boolean isRoutingType; @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "Details in key/value pairs using format details[i].keyname=keyvalue. Example: details[0].hypervisortoolsversion=xenserver61") @@ -80,11 +80,11 @@ public abstract class BaseUpdateTemplateOrIsoCmd extends BaseCmd { @Parameter(name = ApiConstants.CLEAN_UP_DETAILS, type = CommandType.BOOLEAN, - description = "optional boolean field, which indicates if details should be cleaned up or not (if set to true, details removed for this resource, details field ignored; if false or not set, no action)") + description = "Optional boolean field, which indicates if details should be cleaned up or not (if set to true, details removed for this resource, details field ignored; if false or not set, no action)") private Boolean cleanupDetails; @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, - description = "the CPU arch of the template/ISO. Valid options are: x86_64, aarch64", + description = "the CPU arch of the template/ISO. Valid options are: x86_64, aarch64, s390x", since = "4.20") private String arch; @@ -153,8 +153,8 @@ public Map getDetails() { return (Map) (paramsCollection.toArray())[0]; } - public boolean isCleanupDetails(){ - return cleanupDetails == null ? false : cleanupDetails.booleanValue(); + public boolean isCleanupDetails() { + return cleanupDetails != null && cleanupDetails; } public CPU.CPUArch getCPUArch() { diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoPermissionsCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoPermissionsCmd.java index e6ee0897db02..0a62591ddc24 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoPermissionsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoPermissionsCmd.java @@ -40,31 +40,31 @@ protected String getResponseName() { @Parameter(name = ApiConstants.ACCOUNTS, type = CommandType.LIST, collectionType = CommandType.STRING, - description = "a comma delimited list of accounts within caller's domain. If specified, \"op\" parameter has to be passed in.") + description = "A comma delimited list of Accounts within caller's domain. If specified, \"op\" parameter has to be passed in.") private List accountNames; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TemplateResponse.class, required = true, description = "the template ID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TemplateResponse.class, required = true, description = "The Template ID") private Long id; - @Parameter(name = ApiConstants.IS_FEATURED, type = CommandType.BOOLEAN, description = "true for featured template/iso, false otherwise") + @Parameter(name = ApiConstants.IS_FEATURED, type = CommandType.BOOLEAN, description = "True for featured Template/ISO, false otherwise") private Boolean featured; - @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "true for public template/iso, false for private templates/isos") + @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "True for public Template/ISO, false for private Templates/ISOs") private Boolean isPublic; @Parameter(name = ApiConstants.IS_EXTRACTABLE, type = CommandType.BOOLEAN, - description = "true if the template/iso is extractable, false other wise. Can be set only by root admin") + description = "True if the Template/ISO is extractable, false otherwise. Can be set only by root admin") private Boolean isExtractable; - @Parameter(name = ApiConstants.OP, type = CommandType.STRING, description = "permission operator (add, remove, reset)") + @Parameter(name = ApiConstants.OP, type = CommandType.STRING, description = "Permission operator (add, remove, reset)") private String operation; @Parameter(name = ApiConstants.PROJECT_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = ProjectResponse.class, - description = "a comma delimited list of projects. If specified, \"op\" parameter has to be passed in.") + description = "A comma delimited list of projects. If specified, \"op\" parameter has to be passed in.") private List projectIds; // /////////////////////////////////////////////////// @@ -121,7 +121,7 @@ public void execute() { SuccessResponse response = new SuccessResponse(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update template/iso permissions"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update Template/ISO permissions"); } } } 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 8e92e877f5ca..6e880c89432f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java @@ -24,6 +24,8 @@ import org.apache.cloudstack.api.response.ConsoleSessionResponse; import org.apache.cloudstack.consoleproxy.ConsoleSession; +import org.apache.cloudstack.acl.apikeypair.ApiKeyPair; +import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission; import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.api.ApiConstants.HostDetails; @@ -41,6 +43,7 @@ import org.apache.cloudstack.api.response.BackupOfferingResponse; import org.apache.cloudstack.api.response.BackupRepositoryResponse; import org.apache.cloudstack.api.response.BackupScheduleResponse; +import org.apache.cloudstack.api.response.BaseRolePermissionResponse; import org.apache.cloudstack.api.response.BucketResponse; import org.apache.cloudstack.api.response.CapacityResponse; import org.apache.cloudstack.api.response.ClusterResponse; @@ -77,6 +80,7 @@ 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.ApiKeyPairResponse; import org.apache.cloudstack.api.response.LBHealthCheckResponse; import org.apache.cloudstack.api.response.LBStickinessResponse; import org.apache.cloudstack.api.response.ListResponse; @@ -277,7 +281,8 @@ public interface ResponseGenerator { List createUserVmResponse(ResponseView view, String objectName, UserVm... userVms); - List createUserVmResponse(ResponseView view, String objectName, EnumSet details, UserVm... userVms); + List createUserVmResponse(ResponseView view, String objectName, EnumSet details, + UserVm... userVms); SystemVmResponse createSystemVmResponse(VirtualMachine systemVM); @@ -303,11 +308,13 @@ public interface ResponseGenerator { LoadBalancerResponse createLoadBalancerResponse(LoadBalancer loadBalancer); - LBStickinessResponse createLBStickinessPolicyResponse(List stickinessPolicies, LoadBalancer lb); + LBStickinessResponse createLBStickinessPolicyResponse(List stickinessPolicies, + LoadBalancer lb); LBStickinessResponse createLBStickinessPolicyResponse(StickinessPolicy stickinessPolicy, LoadBalancer lb); - LBHealthCheckResponse createLBHealthCheckPolicyResponse(List healthcheckPolicies, LoadBalancer lb); + LBHealthCheckResponse createLBHealthCheckPolicyResponse(List healthcheckPolicies, + LoadBalancer lb); LBHealthCheckResponse createLBHealthCheckPolicyResponse(HealthCheckPolicy healthcheckPolicy, LoadBalancer lb); @@ -315,7 +322,8 @@ public interface ResponseGenerator { PodResponse createMinimalPodResponse(Pod pod); - ZoneResponse createZoneResponse(ResponseView view, DataCenter dataCenter, Boolean showCapacities, Boolean showResourceIcon); + ZoneResponse createZoneResponse(ResponseView view, DataCenter dataCenter, Boolean showCapacities, + Boolean showResourceIcon); DataCenterGuestIpv6PrefixResponse createDataCenterGuestIpv6PrefixResponse(DataCenterGuestIpv6Prefix prefix); @@ -339,6 +347,8 @@ public interface ResponseGenerator { UserVm findUserVmById(Long vmId); + UserVm findUserVmByNicId(Long nicId); + Volume findVolumeById(Long volumeId); Account findAccountByNameDomain(String accountName, Long domainId); @@ -355,7 +365,8 @@ public interface ResponseGenerator { List createTemplateResponses(ResponseView view, long templateId, Long zoneId, boolean readyOnly); - List createTemplateResponses(ResponseView view, long templateId, Long snapshotId, Long volumeId, boolean readyOnly); + List createTemplateResponses(ResponseView view, long templateId, Long snapshotId, Long volumeId, + boolean readyOnly); SecurityGroupResponse createSecurityGroupResponseFromSecurityGroupRule(List securityRules); @@ -374,14 +385,15 @@ public interface ResponseGenerator { TemplateResponse createTemplateUpdateResponse(ResponseView view, VirtualMachineTemplate result); List createTemplateResponses(ResponseView view, VirtualMachineTemplate result, - Long zoneId, boolean readyOnly); + Long zoneId, boolean readyOnly); List createTemplateResponses(ResponseView view, VirtualMachineTemplate result, - List zoneIds, boolean readyOnly); + List zoneIds, boolean readyOnly); List createCapacityResponse(List result, DecimalFormat format); - TemplatePermissionsResponse createTemplatePermissionsResponse(ResponseView view, List accountNames, Long id); + TemplatePermissionsResponse createTemplatePermissionsResponse(ResponseView view, List accountNames, + Long id); AsyncJobResponse queryJobResult(QueryAsyncJobResultCmd cmd); @@ -395,7 +407,8 @@ List createTemplateResponses(ResponseView view, VirtualMachine Long getSecurityGroupId(String groupName, long accountId); - List createIsoResponses(ResponseView view, VirtualMachineTemplate iso, Long zoneId, boolean readyOnly); + List createIsoResponses(ResponseView view, VirtualMachineTemplate iso, Long zoneId, + boolean readyOnly); ProjectResponse createProjectResponse(Project project); @@ -496,13 +509,15 @@ List createTemplateResponses(ResponseView view, VirtualMachine GuestOsMappingResponse createGuestOSMappingResponse(GuestOSHypervisor osHypervisor); - HypervisorGuestOsNamesResponse createHypervisorGuestOSNamesResponse(List> hypervisorGuestOsNames); + HypervisorGuestOsNamesResponse createHypervisorGuestOSNamesResponse( + List> hypervisorGuestOsNames); SnapshotScheduleResponse createSnapshotScheduleResponse(SnapshotSchedule sched); UsageRecordResponse createUsageResponse(Usage usageRecord); - UsageRecordResponse createUsageResponse(Usage usageRecord, Map> resourceTagResponseMap, boolean oldFormat); + UsageRecordResponse createUsageResponse(Usage usageRecord, + Map> resourceTagResponseMap, boolean oldFormat); public Map> getUsageResourceTags(); @@ -514,7 +529,8 @@ List createTemplateResponses(ResponseView view, VirtualMachine public NicResponse createNicResponse(Nic result); - ApplicationLoadBalancerResponse createLoadBalancerContainerReponse(ApplicationLoadBalancerRule lb, Map lbInstances); + ApplicationLoadBalancerResponse createLoadBalancerContainerReponse(ApplicationLoadBalancerRule lb, + Map lbInstances); AffinityGroupResponse createAffinityGroupResponse(AffinityGroup group); @@ -540,9 +556,12 @@ List createTemplateResponses(ResponseView view, VirtualMachine ManagementServerResponse createManagementResponse(ManagementServerHost mgmt); - List createHealthCheckResponse(VirtualMachine router, List healthCheckResults); + List createHealthCheckResponse(VirtualMachine router, + List healthCheckResults); - RollingMaintenanceResponse createRollingMaintenanceResponse(Boolean success, String details, List hostsUpdated, List hostsSkipped); + RollingMaintenanceResponse createRollingMaintenanceResponse(Boolean success, String details, + List hostsUpdated, + List hostsSkipped); ResourceIconResponse createResourceIconResponse(ResourceIcon resourceIcon); @@ -552,11 +571,14 @@ List createTemplateResponses(ResponseView view, VirtualMachine DirectDownloadCertificateResponse createDirectDownloadCertificateResponse(DirectDownloadCertificate certificate); - List createDirectDownloadCertificateHostMapResponse(List hostMappings); + List createDirectDownloadCertificateHostMapResponse( + List hostMappings); - DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateHostStatusResponse(DirectDownloadManager.HostCertificateStatus status); + DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateHostStatusResponse( + DirectDownloadManager.HostCertificateStatus status); - DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateProvisionResponse(Long certificateId, Long hostId, Pair result); + DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateProvisionResponse(Long certificateId, + Long hostId, Pair result); FirewallResponse createIpv6FirewallRuleResponse(FirewallRule acl); @@ -583,4 +605,8 @@ List createTemplateResponses(ResponseView view, VirtualMachine GuiThemeResponse createGuiThemeResponse(GuiThemeJoin guiThemeJoin); ConsoleSessionResponse createConsoleSessionResponse(ConsoleSession consoleSession, ResponseView responseView); + + ApiKeyPairResponse createKeyPairResponse(ApiKeyPair keyPair); + + ListResponse createKeypairPermissionsResponse(List permissions); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/account/CreateAccountCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/account/CreateAccountCmd.java index 6dbc6acc59a9..cc154ed964b3 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/account/CreateAccountCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/account/CreateAccountCmd.java @@ -50,12 +50,12 @@ public class CreateAccountCmd extends BaseCmd { @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, - description = "Name of the account to be created. The user will be added to this newly created account. If no account is specified, the username will be used as the account name.") + description = "Name of the Account to be created. The user will be added to this newly created account. If no Account is specified, the username will be used as the Account name.") private String accountName; @Parameter(name = ApiConstants.ACCOUNT_TYPE, type = CommandType.INTEGER, - description = "Type of the account. Specify 0 for user, 1 for root admin, and 2 for domain admin") + description = "Type of the account. Specify 0 for user, 1 for root admin, and 2 for domain admin") private Integer accountType; @Parameter(name = ApiConstants.ROLE_ID, type = CommandType.UUID, entityType = RoleResponse.class, description = "Creates the account under the specified role.") @@ -64,13 +64,13 @@ public class CreateAccountCmd extends BaseCmd { @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "Creates the user under the specified domain.") private Long domainId; - @Parameter(name = ApiConstants.EMAIL, type = CommandType.STRING, required = true, description = "email") + @Parameter(name = ApiConstants.EMAIL, type = CommandType.STRING, required = true, description = "E-mail") private String email; - @Parameter(name = ApiConstants.FIRSTNAME, type = CommandType.STRING, required = true, description = "firstname") + @Parameter(name = ApiConstants.FIRSTNAME, type = CommandType.STRING, required = true, description = "First name") private String firstName; - @Parameter(name = ApiConstants.LASTNAME, type = CommandType.STRING, required = true, description = "lastname") + @Parameter(name = ApiConstants.LASTNAME, type = CommandType.STRING, required = true, description = "Last name") private String lastName; @Parameter(name = ApiConstants.PASSWORD, @@ -87,16 +87,16 @@ public class CreateAccountCmd extends BaseCmd { @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, required = true, description = "Unique username.") private String userName; - @Parameter(name = ApiConstants.NETWORK_DOMAIN, type = CommandType.STRING, description = "Network domain for the account's networks") + @Parameter(name = ApiConstants.NETWORK_DOMAIN, type = CommandType.STRING, description = "Network domain for the Account's Networks") private String networkDomain; - @Parameter(name = ApiConstants.ACCOUNT_DETAILS, type = CommandType.MAP, description = "details for account used to store specific parameters") + @Parameter(name = ApiConstants.ACCOUNT_DETAILS, type = CommandType.MAP, description = "Details for Account used to store specific parameters") private Map details; - @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.STRING, description = "Account UUID, required for adding account from external provisioning system") + @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.STRING, description = "Account UUID, required for adding Account from external provisioning system") private String accountUUID; - @Parameter(name = ApiConstants.USER_ID, type = CommandType.STRING, description = "User UUID, required for adding account from external provisioning system") + @Parameter(name = ApiConstants.USER_ID, type = CommandType.STRING, description = "User UUID, required for adding Account from external provisioning system") private String userUUID; ///////////////////////////////////////////////////// @@ -177,7 +177,7 @@ public long getEntityOwnerId() { @Override public void execute() { validateParams(); - CallContext.current().setEventDetails("Account Name: " + getUsername() + ", Domain Id:" + getDomainId()); + CallContext.current().setEventDetails("Account Name: " + getUsername() + ", Domain ID:" + getResourceUuid(ApiConstants.DOMAIN_ID)); UserAccount userAccount = _accountService.createUserAccount(this); if (userAccount != null) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/account/DeleteAccountCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/account/DeleteAccountCmd.java index a90fc4aebe9c..c207801e3640 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/account/DeleteAccountCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/account/DeleteAccountCmd.java @@ -35,7 +35,7 @@ import com.cloud.event.EventTypes; import com.cloud.user.Account; -@APICommand(name = "deleteAccount", description = "Deletes a account, and all users associated with this account", responseObject = SuccessResponse.class, entityType = {Account.class}, +@APICommand(name = "deleteAccount", description = "Deletes an Account and all Users associated with this Account", responseObject = SuccessResponse.class, entityType = {Account.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DeleteAccountCmd extends BaseAsyncCmd { @@ -79,8 +79,8 @@ public String getEventType() { @Override public String getEventDescription() { Account account = _accountService.getAccount(getId()); - return (account != null ? "Deleting user account " + account.getAccountName() + " (ID: " + account.getUuid() + ") and all corresponding users" - : "Account delete, but this account does not exist in the system"); + return (account != null ? "Deleting user Account " + account.getAccountName() + " (ID: " + account.getUuid() + ") and all corresponding users" + : "Cannot delete Account - it does not exist in the system"); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/account/DisableAccountCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/account/DisableAccountCmd.java index 55293eca619e..f7f8bd974272 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/account/DisableAccountCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/account/DisableAccountCmd.java @@ -50,13 +50,13 @@ public class DisableAccountCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "Account id") private Long id; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "Disables specified account.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "Disables specified Account.") private String accountName; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "Disables specified account in this domain.") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "Disables specified Account in this domain.") private Long domainId; - @Parameter(name = ApiConstants.LOCK, type = CommandType.BOOLEAN, required = true, description = "If true, only lock the account; else disable the account") + @Parameter(name = ApiConstants.LOCK, type = CommandType.BOOLEAN, required = true, description = "If true, only lock the Account; else disable the Account") private Boolean lockRequested; @Inject @@ -108,19 +108,27 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - return "disabling account: " + getAccountName() + " in domain: " + getDomainId(); + String message = "Disabling Account "; + + if (getId() != null) { + message += "with ID: " + getResourceUuid(ApiConstants.ID); + } else { + message += getAccountName() + " in Domain: " + getResourceUuid(ApiConstants.DOMAIN_ID); + } + + return message; } @Override public void execute() throws ConcurrentOperationException, ResourceUnavailableException { - CallContext.current().setEventDetails("Account Name: " + getAccountName() + ", Domain Id:" + getDomainId()); + CallContext.current().setEventDetails("Account Name: " + getAccountName() + ", Domain Id:" + getResourceUuid(ApiConstants.DOMAIN_ID)); Account result = _regionService.disableAccount(this); if (result != null){ AccountResponse response = _responseGenerator.createAccountResponse(ResponseView.Full, result); response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, lockRequested == true ? "Failed to lock account" : "Failed to disable account"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, lockRequested == true ? "Failed to lock Account" : "Failed to disable Account"); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/account/EnableAccountCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/account/EnableAccountCmd.java index da96383f1345..7478bc8b8116 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/account/EnableAccountCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/account/EnableAccountCmd.java @@ -46,10 +46,10 @@ public class EnableAccountCmd extends BaseCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "Account id") private Long id; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "Enables specified account.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "Enables specified Account.") private String accountName; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "Enables specified account in this domain.") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "Enables specified Account in this domain.") private Long domainId; @Inject @@ -98,7 +98,7 @@ public void execute() { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to enable account"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to enable Account"); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/account/ListAccountsCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/account/ListAccountsCmdByAdmin.java index 09a626ac9547..50e9ba4989cd 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/account/ListAccountsCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/account/ListAccountsCmdByAdmin.java @@ -23,7 +23,7 @@ import com.cloud.user.Account; -@APICommand(name = "listAccounts", description = "Lists accounts and provides detailed account information for listed accounts", responseObject = AccountResponse.class, responseView = ResponseView.Full, entityType = {Account.class}, +@APICommand(name = "listAccounts", description = "Lists Accounts and provides detailed Account information for listed Accounts", responseObject = AccountResponse.class, responseView = ResponseView.Full, entityType = {Account.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class ListAccountsCmdByAdmin extends ListAccountsCmd { } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/account/LockAccountCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/account/LockAccountCmd.java index d7847373e927..3ec191acf846 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/account/LockAccountCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/account/LockAccountCmd.java @@ -28,7 +28,7 @@ import com.cloud.utils.exception.CloudRuntimeException; @APICommand(name = "lockAccount", - description = "This deprecated function used to locks an account. Look for the API DisableAccount instead", + description = "This deprecated function used to lock an Account. Look for the API DisableAccount instead", responseObject = AccountResponse.class, entityType = {Account.class}, requestHasSensitiveInfo = false, @@ -47,7 +47,7 @@ public class LockAccountCmd extends BaseCmd { type = CommandType.UUID, entityType = DomainResponse.class, required = true, - description = "Locks the specified account on this domain.") + description = "Locks the specified Account on this domain.") private Long domainId; ///////////////////////////////////////////////////// @@ -78,6 +78,6 @@ public long getEntityOwnerId() { @Override public void execute() { - throw new CloudRuntimeException("LockAccount does not lock accounts. Its implementation is disabled. Use DisableAccount instead"); + throw new CloudRuntimeException("LockAccount does not lock Accounts. Its implementation is disabled. Use DisableAccount instead."); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/account/UpdateAccountCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/account/UpdateAccountCmd.java index 3347a0d09f37..b6b975ae1ce7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/account/UpdateAccountCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/account/UpdateAccountCmd.java @@ -41,7 +41,7 @@ import com.cloud.user.Account; -@APICommand(name = "updateAccount", description = "Updates account information for the authenticated user", responseObject = AccountResponse.class, entityType = {Account.class}, +@APICommand(name = "updateAccount", description = "Updates Account information for the authenticated user", responseObject = AccountResponse.class, entityType = {Account.class}, responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class UpdateAccountCmd extends BaseCmd implements UserCmd { @@ -52,24 +52,24 @@ public class UpdateAccountCmd extends BaseCmd implements UserCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "Account UUID") private Long id; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "Current account name") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "Current Account name") private String accountName; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "The UUID of the domain where the account exists") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "The UUID of the domain where the Account exists") private Long domainId; - @Parameter(name = ApiConstants.ROLE_ID, type = CommandType.UUID, entityType = RoleResponse.class, description = "The UUID of the dynamic role to set for the account") + @Parameter(name = ApiConstants.ROLE_ID, type = CommandType.UUID, entityType = RoleResponse.class, description = "The UUID of the dynamic role to set for the Account") private Long roleId; - @Parameter(name = ApiConstants.NEW_NAME, type = CommandType.STRING, description = "New name for the account") + @Parameter(name = ApiConstants.NEW_NAME, type = CommandType.STRING, description = "New name for the Account") private String newName; @Parameter(name = ApiConstants.NETWORK_DOMAIN, type = CommandType.STRING, - description = "Network domain for the account's networks; empty string will update domainName with NULL value") + description = "Network domain for the Account's networks; empty string will update domainName with NULL value") private String networkDomain; - @Parameter(name = ApiConstants.ACCOUNT_DETAILS, type = CommandType.MAP, description = "Details for the account used to store specific parameters") + @Parameter(name = ApiConstants.ACCOUNT_DETAILS, type = CommandType.MAP, description = "Details for the Account used to store specific parameters") private Map details; @Parameter(name = ApiConstants.API_KEY_ACCESS, type = CommandType.STRING, description = "Determines if Api key access for this user is enabled, disabled or inherits the value from its parent, the domain level setting api.key.access", since = "4.20.1.0", authorized = {RoleType.Admin}) @@ -144,7 +144,7 @@ public void execute() { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update account"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update Account"); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/CreateRoleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/CreateRoleCmd.java index e67a3e2c0a00..59a09ef7a486 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/CreateRoleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/CreateRoleCmd.java @@ -109,7 +109,7 @@ private void validateRoleParameters() { } if (getRoleId() != null && getRoleId() < 1L) { - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role id provided"); + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role ID provided"); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/CreateRolePermissionCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/CreateRolePermissionCmd.java index 232c4760e1e6..13405431f63e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/CreateRolePermissionCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/CreateRolePermissionCmd.java @@ -81,7 +81,7 @@ public void execute() { if (role == null) { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role id provided"); } - CallContext.current().setEventDetails("Role id: " + role.getId() + ", rule:" + getRule() + ", permission: " + getPermission() + ", description: " + getDescription()); + CallContext.current().setEventDetails("Role ID: " + role.getUuid() + ", rule:" + getRule() + ", permission: " + getPermission() + ", description: " + getDescription()); final RolePermission rolePermission = roleService.createRolePermission(role, getRule(), getPermission(), getDescription()); if (rolePermission == null) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create role permission"); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/DeleteRoleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/DeleteRoleCmd.java index fd2d11aeda0a..80ec08260ab2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/DeleteRoleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/DeleteRoleCmd.java @@ -70,7 +70,7 @@ public void execute() { if (role == null) { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Cannot find the role with provided id"); } - CallContext.current().setEventDetails("Role id: " + role.getId()); + CallContext.current().setEventDetails("Role ID: " + role.getUuid()); boolean result = roleService.deleteRole(role); SuccessResponse response = new SuccessResponse(getCommandName()); response.setSuccess(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/DeleteRolePermissionCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/DeleteRolePermissionCmd.java index bedaca9e23af..cf4a62bf6c43 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/DeleteRolePermissionCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/DeleteRolePermissionCmd.java @@ -68,7 +68,7 @@ public void execute() { if (rolePermission == null) { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role permission id provided"); } - CallContext.current().setEventDetails("Role permission id: " + rolePermission.getId()); + CallContext.current().setEventDetails("Role permission ID: " + rolePermission.getUuid()); boolean result = roleService.deleteRolePermission(rolePermission); SuccessResponse response = new SuccessResponse(getCommandName()); response.setSuccess(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/DisableRoleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/DisableRoleCmd.java index 80cb92c8362f..2c5659b2bc4b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/DisableRoleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/DisableRoleCmd.java @@ -55,7 +55,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE if (role == null) { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Cannot find the role with provided id"); } - CallContext.current().setEventDetails("Role id: " + role.getId()); + CallContext.current().setEventDetails("Role ID: " + role.getUuid()); boolean result = roleService.disableRole(role); SuccessResponse response = new SuccessResponse(getCommandName()); response.setSuccess(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/EnableRoleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/EnableRoleCmd.java index c4a6505d52f6..05dfbe1270fa 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/EnableRoleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/EnableRoleCmd.java @@ -55,7 +55,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE if (role == null) { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Cannot find the role with provided id"); } - CallContext.current().setEventDetails("Role id: " + role.getId()); + CallContext.current().setEventDetails("Role ID: " + role.getUuid()); boolean result = roleService.enableRole(role); SuccessResponse response = new SuccessResponse(getCommandName()); response.setSuccess(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/UpdateRoleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/UpdateRoleCmd.java index 7d002cd889b6..78fe062ac43e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/UpdateRoleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/UpdateRoleCmd.java @@ -46,7 +46,7 @@ public class UpdateRoleCmd extends RoleCmd { description = "ID of the role", validations = {ApiArgValidator.PositiveNumber}) private Long roleId; - @Parameter(name = ApiConstants.NAME, type = BaseCmd.CommandType.STRING, description = "creates a role with this unique name") + @Parameter(name = ApiConstants.NAME, type = BaseCmd.CommandType.STRING, description = "Creates a role with this unique name") private String roleName; @Parameter(name = ApiConstants.DESCRIPTION, type = BaseCmd.CommandType.STRING, description = "The description of the role") diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/UpdateRolePermissionCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/UpdateRolePermissionCmd.java index 3f926092ec3e..992564413f6b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/UpdateRolePermissionCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/UpdateRolePermissionCmd.java @@ -53,7 +53,7 @@ public class UpdateRolePermissionCmd extends BaseCmd { private Long roleId; @Parameter(name = ApiConstants.RULE_ORDER, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = RolePermissionResponse.class, - description = "The parent role permission uuid, use 0 to move this rule at the top of the list") + description = "The parent role permission UUID, use 0 to move this rule at the top of the list") private List rulePermissionOrder; @Parameter(name = ApiConstants.RULE_ID, type = CommandType.UUID, entityType = RolePermissionResponse.class, @@ -111,7 +111,7 @@ public void execute() { if (getRuleId() != null || getRulePermission() != null) { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Parameters permission and ruleid must be mutually exclusive with ruleorder"); } - CallContext.current().setEventDetails("Reordering permissions for role id: " + role.getId()); + CallContext.current().setEventDetails("Reordering permissions for role with ID: " + role.getUuid()); final List rolePermissionsOrder = new ArrayList<>(); for (Long rolePermissionId : getRulePermissionOrder()) { final RolePermission rolePermission = roleService.findRolePermission(rolePermissionId); @@ -129,7 +129,7 @@ public void execute() { if (rolePermission == null) { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid rule id provided"); } - CallContext.current().setEventDetails("Updating permission for rule id: " + getRuleId() + " to: " + getRulePermission().toString()); + CallContext.current().setEventDetails("Updating permission for rule with ID: " + getResourceUuid(ApiConstants.RULE_ID) + " to: " + getRulePermission().toString()); result = roleService.updateRolePermission(role, rolePermission, getRulePermission()); } SuccessResponse response = new SuccessResponse(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/CreateProjectRoleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/CreateProjectRoleCmd.java index ed17a876b248..f71daee5b531 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/CreateProjectRoleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/CreateProjectRoleCmd.java @@ -41,7 +41,7 @@ public class CreateProjectRoleCmd extends ProjectRoleCmd { ///////////////////////////////////////////////////// @Parameter(name = ApiConstants.NAME, type = BaseCmd.CommandType.STRING, required = true, - description = "creates a project role with this unique name") + description = "Creates a project role with this unique name") private String projectRoleName; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/CreateProjectRolePermissionCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/CreateProjectRolePermissionCmd.java index d39c2312aa91..e085c10cee0b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/CreateProjectRolePermissionCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/CreateProjectRolePermissionCmd.java @@ -72,7 +72,7 @@ public void execute() { if (projectRole == null) { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid project role ID provided"); } - CallContext.current().setEventDetails("Project Role ID: " + projectRole.getId() + ", Rule:" + getRule() + ", Permission: " + getPermission() + ", Description: " + getDescription()); + CallContext.current().setEventDetails("Project Role ID: " + projectRole.getUuid() + ", Rule:" + getRule() + ", Permission: " + getPermission() + ", Description: " + getDescription()); final ProjectRolePermission projectRolePermission = projRoleService.createProjectRolePermission(this); if (projectRolePermission == null) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create project role permission"); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/DeleteProjectRoleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/DeleteProjectRoleCmd.java index 9f8d82489584..84f73e7a1a32 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/DeleteProjectRoleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/DeleteProjectRoleCmd.java @@ -69,7 +69,7 @@ public void execute() { if (role == null) { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Cannot find project role with provided id"); } - CallContext.current().setEventDetails("Deleting Project Role with id: " + role.getId()); + CallContext.current().setEventDetails("Deleting Project Role with ID: " + role.getUuid()); boolean result = projRoleService.deleteProjectRole(role, getProjectId()); SuccessResponse response = new SuccessResponse(getCommandName()); response.setSuccess(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/DeleteProjectRolePermissionCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/DeleteProjectRolePermissionCmd.java index ac68278535e2..d7941a6a4cc3 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/DeleteProjectRolePermissionCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/DeleteProjectRolePermissionCmd.java @@ -70,7 +70,7 @@ public void execute() { if (rolePermission == null || rolePermission.getProjectId() != getProjectId()) { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role permission id provided for the project"); } - CallContext.current().setEventDetails("Deleting Project Role permission with id: " + rolePermission.getId()); + CallContext.current().setEventDetails("Deleting Project Role permission with ID: " + rolePermission.getUuid()); boolean result = projRoleService.deleteProjectRolePermission(rolePermission); SuccessResponse response = new SuccessResponse(); response.setSuccess(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/UpdateProjectRoleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/UpdateProjectRoleCmd.java index 3bc8b3d61868..80dbfd71275f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/UpdateProjectRoleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/UpdateProjectRoleCmd.java @@ -43,7 +43,7 @@ public class UpdateProjectRoleCmd extends ProjectRoleCmd { private Long id; @Parameter(name = ApiConstants.NAME, type = BaseCmd.CommandType.STRING, - description = "creates a project role with this unique name", validations = {ApiArgValidator.NotNullOrEmpty}) + description = "Creates a project role with this unique name", validations = {ApiArgValidator.NotNullOrEmpty}) private String projectRoleName; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/UpdateProjectRolePermissionCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/UpdateProjectRolePermissionCmd.java index dd59310c66ae..fd0c043f2321 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/UpdateProjectRolePermissionCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/UpdateProjectRolePermissionCmd.java @@ -57,7 +57,7 @@ public class UpdateProjectRolePermissionCmd extends BaseCmd { private Long projectId; @Parameter(name = ApiConstants.RULE_ORDER, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = ProjectRolePermissionResponse.class, - description = "The parent role permission uuid, use 0 to move this rule at the top of the list") + description = "ID of the parent role permission, use 0 to move this rule at the top of the list") private List projectRulePermissionOrder; @Parameter(name = ApiConstants.PROJECT_ROLE_PERMISSION_ID, type = CommandType.UUID, entityType = ProjectRolePermissionResponse.class, @@ -115,7 +115,7 @@ public void execute() { if (getProjectRuleId() != null || getProjectRolePermission() != null) { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Parameters permission and ruleid must be mutually exclusive with ruleorder"); } - CallContext.current().setEventDetails("Reordering permissions for role id: " + projectRole.getId()); + CallContext.current().setEventDetails("Reordering permissions for role with ID: " + projectRole.getUuid()); result = updateProjectRolePermissionOrder(projectRole); } else if (getProjectRuleId() != null || getProjectRolePermission() != null ) { @@ -123,7 +123,7 @@ public void execute() { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Parameters permission and ruleid must be mutually exclusive with ruleorder"); } ProjectRolePermission rolePermission = getValidProjectRolePermission(); - CallContext.current().setEventDetails("Updating project role permission for rule id: " + getProjectRuleId() + " to: " + getProjectRolePermission().toString()); + CallContext.current().setEventDetails("Updating project role permission for rule ID: " + getProjectRuleId() + " to: " + getProjectRolePermission().toString()); result = projRoleService.updateProjectRolePermission(projectId, projectRole, rolePermission, getProjectRolePermission()); } SuccessResponse response = new SuccessResponse(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/address/AcquirePodIpCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/address/AcquirePodIpCmdByAdmin.java index 7397697bd2cc..88c48103c1b4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/address/AcquirePodIpCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/address/AcquirePodIpCmdByAdmin.java @@ -40,7 +40,7 @@ public class AcquirePodIpCmdByAdmin extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.STRING, entityType = ZoneResponse.class, required = true, description = "the ID of the zone") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.STRING, entityType = ZoneResponse.class, required = true, description = "The ID of the zone") private String zoneId; @Parameter(name = ApiConstants.POD_ID, type = CommandType.STRING, entityType = ZoneResponse.class, required = false, description = "Pod ID") diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/address/AssociateIPAddrCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/address/AssociateIPAddrCmdByAdmin.java index 672691ffbd8f..a34de31f78e1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/address/AssociateIPAddrCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/address/AssociateIPAddrCmdByAdmin.java @@ -23,7 +23,7 @@ import org.apache.cloudstack.api.command.user.address.AssociateIPAddrCmd; import org.apache.cloudstack.api.response.IPAddressResponse; -@APICommand(name = "associateIpAddress", description = "Acquires and associates a public IP to an account.", responseObject = IPAddressResponse.class, responseView = ResponseView.Full, +@APICommand(name = "associateIpAddress", description = "Acquires and associates a public IP to an Account.", responseObject = IPAddressResponse.class, responseView = ResponseView.Full, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class AssociateIPAddrCmdByAdmin extends AssociateIPAddrCmd implements AdminCmd { } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/address/ListPublicIpAddressesCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/address/ListPublicIpAddressesCmdByAdmin.java index 4bd6aa7227c8..9976747e1b62 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/address/ListPublicIpAddressesCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/address/ListPublicIpAddressesCmdByAdmin.java @@ -24,6 +24,6 @@ import com.cloud.network.IpAddress; -@APICommand(name = "listPublicIpAddresses", description = "Lists all public ip addresses", responseObject = IPAddressResponse.class, responseView = ResponseView.Full, +@APICommand(name = "listPublicIpAddresses", description = "Lists all public IP addresses", responseObject = IPAddressResponse.class, responseView = ResponseView.Full, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, entityType = {IpAddress.class}) public class ListPublicIpAddressesCmdByAdmin extends ListPublicIpAddressesCmd implements AdminCmd {} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/address/ReleasePodIpCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/address/ReleasePodIpCmdByAdmin.java index 7d4cab6a0ac4..266c8eecd58c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/address/ReleasePodIpCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/address/ReleasePodIpCmdByAdmin.java @@ -70,7 +70,7 @@ public void execute() { response.setDisplayText("IP is released successfully"); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to release Pod ip "); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to release Pod IP"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/affinitygroup/UpdateVMAffinityGroupCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/affinitygroup/UpdateVMAffinityGroupCmdByAdmin.java index 43e70838e18f..fbe2d3cc0bd2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/affinitygroup/UpdateVMAffinityGroupCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/affinitygroup/UpdateVMAffinityGroupCmdByAdmin.java @@ -26,7 +26,7 @@ import com.cloud.vm.VirtualMachine; -@APICommand(name = "updateVMAffinityGroup", description = "Updates the affinity/anti-affinity group associations of a virtual machine. The VM has to be stopped and restarted for the " +@APICommand(name = "updateVMAffinityGroup", description = "Updates the affinity/anti-affinity group associations of an Instance. The Instance has to be stopped and restarted for the " + "new properties to take effect.", responseObject = UserVmResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/annotation/AddAnnotationCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/annotation/AddAnnotationCmd.java index c2ded921c401..834fda1834bb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/annotation/AddAnnotationCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/annotation/AddAnnotationCmd.java @@ -33,22 +33,22 @@ import org.apache.cloudstack.context.CallContext; import org.apache.commons.lang3.BooleanUtils; -@APICommand(name = "addAnnotation", description = "add an annotation.", responseObject = AnnotationResponse.class, +@APICommand(name = "addAnnotation", description = "Add an annotation.", responseObject = AnnotationResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.11", authorized = {RoleType.Admin}) public class AddAnnotationCmd extends BaseCmd { - @Parameter(name = ApiConstants.ANNOTATION, type = CommandType.STRING, description = "the annotation text") + @Parameter(name = ApiConstants.ANNOTATION, type = CommandType.STRING, description = "The annotation text") private String annotation; @Parameter(name = ApiConstants.ENTITY_TYPE, type = CommandType.STRING, description = "The following entity types are allowed VM, VOLUME, SNAPSHOT, VM_SNAPSHOT, INSTANCE_GROUP, SSH_KEYPAIR, USER_DATA, NETWORK, VPC, PUBLIC_IP_ADDRESS, VPN_CUSTOMER_GATEWAY, TEMPLATE, ISO, KUBERNETES_CLUSTER, SERVICE_OFFERING, DISK_OFFERING, NETWORK_OFFERING, ZONE, POD, CLUSTER, HOST, DOMAIN, PRIMARY_STORAGE, SECONDARY_STORAGE, VR, SYSTEM_VM, AUTOSCALE_VM_GROUP, MANAGEMENT_SERVER") private String entityType; - @Parameter(name = ApiConstants.ENTITY_ID, type = CommandType.STRING, description = "the id of the entity to annotate") + @Parameter(name = ApiConstants.ENTITY_ID, type = CommandType.STRING, description = "The ID of the entity to annotate") private String entityUuid; @Parameter(name = ApiConstants.ADMINS_ONLY, type = CommandType.BOOLEAN, since = "4.16.0", - description = "the annotation is visible for admins only") + description = "The annotation is visible for admins only") private Boolean adminsOnly; public String getAnnotation() { @@ -77,7 +77,7 @@ public boolean isAdminsOnly() { public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { - Preconditions.checkNotNull(getEntityUuid(),"I have to have an entity to set an annotation on!"); + Preconditions.checkNotNull(getEntityUuid(),"I need to have an entity to set an annotation on!"); Preconditions.checkState(AnnotationService.EntityType.contains(entityType),(java.lang.String)"'%s' is not a valid EntityType to put annotations on", entityType); AnnotationResponse annotationResponse = annotationService.addAnnotation(this); annotationResponse.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/annotation/ListAnnotationsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/annotation/ListAnnotationsCmd.java index 3df4536786f4..fcaba5154e1f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/annotation/ListAnnotationsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/annotation/ListAnnotationsCmd.java @@ -37,22 +37,22 @@ public class ListAnnotationsCmd extends BaseListCmd { - @Parameter(name = ApiConstants.ID, type = CommandType.STRING, description = "the id of the annotation") + @Parameter(name = ApiConstants.ID, type = CommandType.STRING, description = "The ID of the annotation") private String uuid; - @Parameter(name = ApiConstants.ENTITY_TYPE, type = CommandType.STRING, description = "the entity type") + @Parameter(name = ApiConstants.ENTITY_TYPE, type = CommandType.STRING, description = "The entity type") private String entityType; - @Parameter(name = ApiConstants.ENTITY_ID, type = CommandType.STRING, description = "the id of the entity for which to show annotations") + @Parameter(name = ApiConstants.ENTITY_ID, type = CommandType.STRING, description = "The ID of the entity for which to show annotations") private String entityUuid; @Parameter(name = ApiConstants.USER_ID, type = CommandType.STRING, since = "4.16.0", - description = "optional: the id of the user of the annotation", required = false) + description = "Optional: The ID of the user of the annotation", required = false) private String userUuid; @Parameter(name = ApiConstants.ANNOTATION_FILTER, type = CommandType.STRING, since = "4.16.0", - description = "possible values are \"self\" and \"all\". " + description = "Possible values are \"self\" and \"all\". " + "* self : annotations that have been created by the calling user. " + "* all : all the annotations the calling user can access") private String annotationFilter; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/annotation/RemoveAnnotationCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/annotation/RemoveAnnotationCmd.java index 693ad09bfa9b..d173c35289f2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/annotation/RemoveAnnotationCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/annotation/RemoveAnnotationCmd.java @@ -30,12 +30,12 @@ import org.apache.cloudstack.api.response.AnnotationResponse; import org.apache.cloudstack.context.CallContext; -@APICommand(name = "removeAnnotation", description = "remove an annotation.", responseObject = AnnotationResponse.class, +@APICommand(name = "removeAnnotation", description = "Remove an annotation.", responseObject = AnnotationResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.11", authorized = {RoleType.Admin}) public class RemoveAnnotationCmd extends BaseCmd { - @Parameter(name = ApiConstants.ID, type = CommandType.STRING, required = true, description = "the id of the annotation") + @Parameter(name = ApiConstants.ID, type = CommandType.STRING, required = true, description = "The ID of the annotation") private String uuid; public String getUuid() { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/annotation/UpdateAnnotationVisibilityCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/annotation/UpdateAnnotationVisibilityCmd.java index b1b7295510cd..d0bd7042ead8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/annotation/UpdateAnnotationVisibilityCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/annotation/UpdateAnnotationVisibilityCmd.java @@ -30,7 +30,7 @@ import org.apache.cloudstack.api.response.AnnotationResponse; import org.apache.cloudstack.context.CallContext; -@APICommand(name = "updateAnnotationVisibility", description = "update an annotation visibility.", +@APICommand(name = "updateAnnotationVisibility", description = "Update an annotation visibility.", responseObject = AnnotationResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.16", authorized = {RoleType.Admin}) @@ -38,11 +38,11 @@ public class UpdateAnnotationVisibilityCmd extends BaseCmd { @Parameter(name = ApiConstants.ID, type = CommandType.STRING, required = true, - description = "the id of the annotation") + description = "The ID of the annotation") private String uuid; @Parameter(name = ApiConstants.ADMINS_ONLY, type = CommandType.BOOLEAN, required = true, - description = "the annotation is visible for admins only") + description = "The annotation is visible for admins only") private Boolean adminsOnly; public String getUuid() { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/autoscale/CreateCounterCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/autoscale/CreateCounterCmd.java index 7fa66ffff1f4..d7be56bf3f46 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/autoscale/CreateCounterCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/autoscale/CreateCounterCmd.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.admin.autoscale; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; @@ -31,7 +32,7 @@ import com.cloud.network.as.Counter; import com.cloud.user.Account; -@APICommand(name = "createCounter", description = "Adds metric counter for VM auto scaling", responseObject = CounterResponse.class, +@APICommand(name = "createCounter", description = "Adds metric counter for Instance auto scaling", responseObject = CounterResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateCounterCmd extends BaseAsyncCreateCmd { private static final String s_name = "counterresponse"; @@ -89,9 +90,6 @@ public void create() { if (ctr != null) { this.setEntityId(ctr.getId()); this.setEntityUuid(ctr.getUuid()); - CounterResponse response = _responseGenerator.createCounterResponse(ctr); - response.setResponseName(getCommandName()); - this.setResponseObject(response); } else { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Counter with name " + getName()); } @@ -99,6 +97,11 @@ public void create() { @Override public void execute() { + CallContext.current().setEventDetails("Counter ID: " + getEntityUuid()); + Counter ctr = _autoScaleService.getCounter(getEntityId()); + CounterResponse response = _responseGenerator.createCounterResponse(ctr); + response.setResponseName(getCommandName()); + this.setResponseObject(response); } @Override @@ -113,7 +116,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "creating a new Counter"; + return "Creating a new Counter"; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/autoscale/DeleteCounterCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/autoscale/DeleteCounterCmd.java index b7b2ce5cb70d..8e941965e84b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/autoscale/DeleteCounterCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/autoscale/DeleteCounterCmd.java @@ -32,7 +32,7 @@ import com.cloud.exception.ResourceInUseException; import com.cloud.user.Account; -@APICommand(name = "deleteCounter", description = "Deletes a counter for VM auto scaling", responseObject = SuccessResponse.class, +@APICommand(name = "deleteCounter", description = "Deletes a counter for Instance auto scaling", responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DeleteCounterCmd extends BaseAsyncCmd { @@ -40,7 +40,7 @@ public class DeleteCounterCmd extends BaseAsyncCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = CounterResponse.class, required = true, description = "the ID of the counter") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = CounterResponse.class, required = true, description = "The ID of the counter") private Long id; // /////////////////////////////////////////////////// @@ -61,7 +61,7 @@ public void execute() { SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); } else { - logger.warn("Failed to delete counter with Id: " + getId()); + logger.warn("Failed to delete counter with Id: {}", getId()); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete counter."); } } @@ -91,6 +91,6 @@ public String getEventType() { @Override public String getEventDescription() { - return "Deleting a counter."; + return "Deleting auto scaling counter with ID: " + getResourceUuid(ApiConstants.ID); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/CloneBackupOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/CloneBackupOfferingCmd.java new file mode 100644 index 000000000000..500a77f3d4fc --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/CloneBackupOfferingCmd.java @@ -0,0 +1,166 @@ +// 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.backup; + +import javax.inject.Inject; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.offering.DomainAndZoneIdResolver; +import org.apache.cloudstack.api.response.BackupOfferingResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.backup.BackupManager; +import org.apache.cloudstack.backup.BackupOffering; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.event.EventTypes; +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.utils.exception.CloudRuntimeException; + +import java.util.Arrays; +import java.util.List; +import java.util.function.LongFunction; + +@APICommand(name = "cloneBackupOffering", + description = "Clones a backup offering from an existing offering", + responseObject = BackupOfferingResponse.class, since = "4.23.0", + authorized = {RoleType.Admin}) +public class CloneBackupOfferingCmd extends BaseAsyncCmd implements DomainAndZoneIdResolver { + + @Inject + protected BackupManager backupManager; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + //////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.SOURCE_OFFERING_ID, type = BaseCmd.CommandType.UUID, entityType = BackupOfferingResponse.class, + required = true, description = "The ID of the source backup offering to clone from") + private Long sourceOfferingId; + + @Parameter(name = ApiConstants.NAME, type = BaseCmd.CommandType.STRING, required = true, + description = "The name of the cloned offering") + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, type = BaseCmd.CommandType.STRING, required = false, + description = "The description of the cloned offering") + private String description; + + @Parameter(name = ApiConstants.EXTERNAL_ID, type = BaseCmd.CommandType.STRING, required = false, + description = "The backup offering ID (from backup provider side)") + private String externalId; + + @Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, entityType = ZoneResponse.class, + description = "The zone ID", required = false) + private Long zoneId; + + @Parameter(name = ApiConstants.DOMAIN_ID, + type = CommandType.STRING, + description = "the ID of the containing domain(s) as comma separated string, public for public offerings", + length = 4096) + private String domainIds; + + @Parameter(name = ApiConstants.ALLOW_USER_DRIVEN_BACKUPS, type = BaseCmd.CommandType.BOOLEAN, + description = "Whether users are allowed to create adhoc backups and backup schedules", required = false) + private Boolean userDrivenBackups; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getSourceOfferingId() { + return sourceOfferingId; + } + + public String getName() { + return name; + } + + public String getExternalId() { + return externalId; + } + + public Long getZoneId() { + return zoneId; + } + + public String getDescription() { + return description; + } + + public Boolean getUserDrivenBackups() { + return userDrivenBackups; + } + + public List getDomainIds() { + if (domainIds != null && !domainIds.isEmpty()) { + return Arrays.asList(Arrays.stream(domainIds.split(",")).map(domainId -> Long.parseLong(domainId.trim())).toArray(Long[]::new)); + } + LongFunction> defaultDomainsProvider = null; + if (backupManager != null) { + defaultDomainsProvider = backupManager::getBackupOfferingDomains; + } + return resolveDomainIds(domainIds, sourceOfferingId, defaultDomainsProvider, "backup offering"); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + BackupOffering policy = backupManager.cloneBackupOffering(this); + if (policy == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to clone backup offering"); + } + BackupOfferingResponse response = _responseGenerator.createBackupOfferingResponse(policy); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (InvalidParameterValueException e) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, e.getMessage()); + } catch (CloudRuntimeException e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_VM_BACKUP_OFFERING_CLONE; + } + + @Override + public String getEventDescription() { + return "Cloning backup offering: " + name + " from source offering: " + (sourceOfferingId == null ? "" : sourceOfferingId.toString()); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/CreateImageTransferCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/CreateImageTransferCmd.java new file mode 100644 index 000000000000..c98bfb850529 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/CreateImageTransferCmd.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 +//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.backup; + +import javax.inject.Inject; + +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.command.admin.AdminCmd; +import org.apache.cloudstack.api.response.BackupResponse; +import org.apache.cloudstack.api.response.ImageTransferResponse; +import org.apache.cloudstack.api.response.VolumeResponse; +import org.apache.cloudstack.backup.ImageTransfer; +import org.apache.cloudstack.backup.KVMBackupExportService; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.utils.EnumUtils; + +@APICommand(name = "createImageTransfer", + description = "Create image transfer for a disk in backup. This API is intended for testing only and is disabled by default.", + responseObject = ImageTransferResponse.class, + since = "4.23.0", + authorized = {RoleType.Admin}) +public class CreateImageTransferCmd extends BaseCmd implements AdminCmd { + + @Inject + private KVMBackupExportService kvmBackupExportService; + + @Parameter(name = ApiConstants.BACKUP_ID, + type = CommandType.UUID, + entityType = BackupResponse.class, + description = "ID of the backup") + private Long backupId; + + @Parameter(name = ApiConstants.VOLUME_ID, + type = CommandType.UUID, + entityType = VolumeResponse.class, + required = true, + description = "ID of the disk/volume") + private Long volumeId; + + @Parameter(name = ApiConstants.DIRECTION, + type = CommandType.STRING, + required = true, + description = "Direction of the transfer: upload, download") + private String direction; + + @Parameter(name = ApiConstants.FORMAT, + type = CommandType.STRING, + description = "Format for the image transfer: raw/cow. 'raw' will create an NBD backend. 'cow' will use the File backend." + + "For download, only the 'raw' format is supported. Default: raw") + private String format; + + public Long getBackupId() { + return backupId; + } + + public Long getVolumeId() { + return volumeId; + } + + public ImageTransfer.Direction getDirection() { + return ImageTransfer.Direction.valueOf(direction); + } + + public ImageTransfer.Format getFormat() { + return EnumUtils.getEnum(ImageTransfer.Format.class, format); + } + + @Override + public void execute() { + ImageTransferResponse response = kvmBackupExportService.createImageTransfer(this); + response.setObjectName(ImageTransfer.class.getSimpleName().toLowerCase()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/DeleteVmCheckpointCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/DeleteVmCheckpointCmd.java new file mode 100644 index 000000000000..d0e17e86d427 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/DeleteVmCheckpointCmd.java @@ -0,0 +1,85 @@ +//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.api.command.admin.backup; + +import javax.inject.Inject; + +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.command.admin.AdminCmd; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.backup.KVMBackupExportService; +import org.apache.cloudstack.context.CallContext; + +@APICommand(name = "deleteVirtualMachineCheckpoint", + description = "Delete a VM checkpoint. This API is intended for testing only and is disabled by default.", + responseObject = SuccessResponse.class, + since = "4.23.0", + authorized = {RoleType.Admin}) +public class DeleteVmCheckpointCmd extends BaseCmd implements AdminCmd { + + @Inject + private KVMBackupExportService kvmBackupExportService; + + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, + type = CommandType.UUID, + entityType = UserVmResponse.class, + required = true, + description = "ID of the VM") + private Long vmId; + + @Parameter(name = "checkpointid", + type = CommandType.STRING, + required = true, + description = "Checkpoint ID") + private String checkpointId; + + public Long getVmId() { + return vmId; + } + + public String getCheckpointId() { + return checkpointId; + } + + public void setVmId(Long vmId) { + this.vmId = vmId; + } + + public void setCheckpointId(String checkpointId) { + this.checkpointId = checkpointId; + } + + @Override + public void execute() { + boolean result = kvmBackupExportService.deleteVmCheckpoint(this); + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setSuccess(result); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/FinalizeBackupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/FinalizeBackupCmd.java new file mode 100644 index 000000000000..45173f8668ee --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/FinalizeBackupCmd.java @@ -0,0 +1,103 @@ +//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.api.command.admin.backup; + +import javax.inject.Inject; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.admin.AdminCmd; +import org.apache.cloudstack.api.response.BackupResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.backup.Backup; +import org.apache.cloudstack.backup.BackupManager; +import org.apache.cloudstack.backup.KVMBackupExportService; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.event.EventTypes; + +@APICommand(name = "finalizeBackup", + description = "Finalize a VM backup session. This API is intended for testing only and is disabled by default.", + responseObject = BackupResponse.class, + since = "4.23.0", + authorized = {RoleType.Admin}) +public class FinalizeBackupCmd extends BaseAsyncCmd implements AdminCmd { + + @Inject + private KVMBackupExportService kvmBackupExportService; + + @Inject + private BackupManager backupManager; + + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, + type = CommandType.UUID, + entityType = UserVmResponse.class, + required = true, + description = "ID of the VM") + private Long vmId; + + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + entityType = BackupResponse.class, + required = true, + description = "ID of the backup") + private Long backupId; + + public Long getVmId() { + return vmId; + } + + public Long getBackupId() { + return backupId; + } + + @Override + public void execute() { + Backup backup = kvmBackupExportService.finalizeBackup(this); + + if (backup == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Backup"); + } + + BackupResponse response = backupManager.createBackupResponse(backup, null); + + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + + @Override + public String getEventType() { + return EventTypes.EVENT_VM_BACKUP_CREATE; + } + + @Override + public String getEventDescription() { + return "Finalizing backup " + backupId; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/FinalizeImageTransferCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/FinalizeImageTransferCmd.java new file mode 100644 index 000000000000..dfc43e233bf2 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/FinalizeImageTransferCmd.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 +//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.backup; + +import javax.inject.Inject; + +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.command.admin.AdminCmd; +import org.apache.cloudstack.api.response.ImageTransferResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.backup.ImageTransfer; +import org.apache.cloudstack.backup.KVMBackupExportService; +import org.apache.cloudstack.context.CallContext; + +@APICommand(name = "finalizeImageTransfer", + description = "Finalize an image transfer. This API is intended for testing only and is disabled by default.", + responseObject = SuccessResponse.class, + since = "4.23.0", + authorized = {RoleType.Admin}) +public class FinalizeImageTransferCmd extends BaseCmd implements AdminCmd { + + @Inject + private KVMBackupExportService kvmBackupExportService; + + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + entityType = ImageTransferResponse.class, + required = true, + description = "ID of the image transfer") + private Long imageTransferId; + + public Long getImageTransferId() { + return imageTransferId; + } + + @Override + public void execute() { + boolean result = kvmBackupExportService.finalizeImageTransfer(this); + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setSuccess(result); + response.setObjectName(ImageTransfer.class.getSimpleName().toLowerCase()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ImportBackupOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ImportBackupOfferingCmd.java index 7d3902bc4902..4cf27c561508 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ImportBackupOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ImportBackupOfferingCmd.java @@ -27,6 +27,7 @@ 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.DomainResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.backup.BackupManager; import org.apache.cloudstack.backup.BackupOffering; @@ -40,6 +41,11 @@ import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.commons.collections.CollectionUtils; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; @APICommand(name = "importBackupOffering", description = "Imports a backup offering using a backup provider", @@ -48,18 +54,18 @@ public class ImportBackupOfferingCmd extends BaseAsyncCmd { @Inject - private BackupManager backupManager; + protected BackupManager backupManager; ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// //////////////////////////////////////////////////// @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, - description = "the name of the backup offering") + description = "The name of the backup offering") private String name; @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, required = true, - description = "the description of the backup offering") + description = "The description of the backup offering") private String description; @Parameter(name = ApiConstants.EXTERNAL_ID, @@ -76,6 +82,14 @@ public class ImportBackupOfferingCmd extends BaseAsyncCmd { description = "Whether users are allowed to create adhoc backups and backup schedules", required = true) private Boolean userDrivenBackups; + @Parameter(name = ApiConstants.DOMAIN_ID, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = DomainResponse.class, + description = "the ID of the containing domain(s), null for public offerings", + since = "4.23.0") + private List domainIds; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -100,6 +114,15 @@ public Boolean getUserDrivenBackups() { return userDrivenBackups == null ? false : userDrivenBackups; } + public List getDomainIds() { + if (CollectionUtils.isNotEmpty(domainIds)) { + Set set = new LinkedHashSet<>(domainIds); + domainIds.clear(); + domainIds.addAll(set); + } + return domainIds; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -134,6 +157,6 @@ public String getEventType() { @Override public String getEventDescription() { - return "Importing backup offering: " + name + " (external ID: " + externalId + ") on zone ID " + zoneId ; + return "Importing backup offering: " + name + " (external ID: " + externalId + ") on zone with ID: " + getResourceUuid(ApiConstants.ZONE_ID) ; } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ListImageTransfersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ListImageTransfersCmd.java new file mode 100644 index 000000000000..d810d21ab5f8 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ListImageTransfersCmd.java @@ -0,0 +1,81 @@ +//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.api.command.admin.backup; + +import java.util.List; + +import javax.inject.Inject; + +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.command.admin.AdminCmd; +import org.apache.cloudstack.api.response.BackupResponse; +import org.apache.cloudstack.api.response.ImageTransferResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.backup.ImageTransfer; +import org.apache.cloudstack.backup.KVMBackupExportService; +import org.apache.cloudstack.context.CallContext; + +@APICommand(name = "listImageTransfers", + description = "List image transfers for a backup. This API is intended for testing only and is disabled by default.", + responseObject = ImageTransferResponse.class, + since = "4.23.0", + authorized = {RoleType.Admin}) +public class ListImageTransfersCmd extends BaseListCmd implements AdminCmd { + + @Inject + private KVMBackupExportService kvmBackupExportService; + + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + entityType = ImageTransferResponse.class, + description = "ID of the Image Transfer") + private Long id; + + @Parameter(name = ApiConstants.BACKUP_ID, + type = CommandType.UUID, + entityType = BackupResponse.class, + description = "ID of the backup") + private Long backupId; + + public Long getId() { + return id; + } + + public Long getBackupId() { + return backupId; + } + + @Override + public void execute() { + List responses = kvmBackupExportService.listImageTransfers(this); + ListResponse response = new ListResponse<>(); + response.setResponses(responses); + response.setObjectName(ImageTransfer.class.getSimpleName().toLowerCase()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ListVmCheckpointsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ListVmCheckpointsCmd.java new file mode 100644 index 000000000000..a61661e982de --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ListVmCheckpointsCmd.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 +//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.backup; + +import java.util.List; + +import javax.inject.Inject; + +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.command.admin.AdminCmd; +import org.apache.cloudstack.api.response.CheckpointResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.backup.KVMBackupExportService; + +@APICommand(name = "listVirtualMachineCheckpoints", + description = "List checkpoints for a VM. This API is intended for testing only and is disabled by default.", + responseObject = CheckpointResponse.class, + since = "4.23.0", + authorized = {RoleType.Admin}) +public class ListVmCheckpointsCmd extends BaseListCmd implements AdminCmd { + + @Inject + private KVMBackupExportService kvmBackupExportService; + + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, + type = CommandType.UUID, + entityType = UserVmResponse.class, + required = true, + description = "ID of the VM") + private Long vmId; + + public Long getVmId() { + return vmId; + } + + @Override + public void execute() { + List responses = kvmBackupExportService.listVmCheckpoints(this); + ListResponse response = new ListResponse<>(); + response.setResponses(responses); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return 0; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/StartBackupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/StartBackupCmd.java new file mode 100644 index 000000000000..1bf6d45db049 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/StartBackupCmd.java @@ -0,0 +1,120 @@ +//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.api.command.admin.backup; + +import javax.inject.Inject; + +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.BaseAsyncCreateCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.admin.AdminCmd; +import org.apache.cloudstack.api.response.BackupResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.backup.Backup; +import org.apache.cloudstack.backup.BackupManager; +import org.apache.cloudstack.backup.KVMBackupExportService; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.event.EventTypes; + +@APICommand(name = "startBackup", + description = "Start a VM backup session using pull mode backup-begin on the KVM host. This API is intended for testing only and is disabled by default.", + responseObject = BackupResponse.class, + since = "4.23.0", + authorized = {RoleType.Admin}) + public class StartBackupCmd extends BaseAsyncCreateCmd implements AdminCmd { + + @Inject + private KVMBackupExportService kvmBackupExportService; + + @Inject + private BackupManager backupManager; + + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, + type = CommandType.UUID, + entityType = UserVmResponse.class, + required = true, + description = "ID of the VM") + private Long vmId; + + @Parameter(name = ApiConstants.NAME, + type = CommandType.STRING, + description = "the name of the backup") + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, + type = CommandType.STRING, + description = "the description for the backup") + private String description; + + public Long getVmId() { + return vmId; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + @Override + public void execute() { + try { + Backup backup = kvmBackupExportService.startBackup(this); + BackupResponse response = backupManager.createBackupResponse(backup, null); + + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + @Override + public void create() { + Backup backup = kvmBackupExportService.createBackup(this); + + if (backup != null) { + setEntityId(backup.getId()); + setEntityUuid(backup.getUuid()); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Backup"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_VM_BACKUP_CREATE; + } + + @Override + public String getEventDescription() { + return "Starting backup for Instance " + vmId; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/UpdateBackupOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/UpdateBackupOfferingCmd.java index 9de06715ee74..2f0dd6acd0e1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/UpdateBackupOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/UpdateBackupOfferingCmd.java @@ -25,19 +25,24 @@ import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.offering.DomainAndZoneIdResolver; import org.apache.cloudstack.api.response.BackupOfferingResponse; import org.apache.cloudstack.backup.BackupManager; import org.apache.cloudstack.backup.BackupOffering; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import com.cloud.exception.InvalidParameterValueException; import com.cloud.user.Account; import com.cloud.utils.exception.CloudRuntimeException; +import java.util.List; +import java.util.function.LongFunction; + @APICommand(name = "updateBackupOffering", description = "Updates a backup offering.", responseObject = BackupOfferingResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.16.0") -public class UpdateBackupOfferingCmd extends BaseCmd { +public class UpdateBackupOfferingCmd extends BaseCmd implements DomainAndZoneIdResolver { @Inject private BackupManager backupManager; @@ -57,6 +62,13 @@ public class UpdateBackupOfferingCmd extends BaseCmd { @Parameter(name = ApiConstants.ALLOW_USER_DRIVEN_BACKUPS, type = CommandType.BOOLEAN, description = "Whether to allow user driven backups or not") private Boolean allowUserDrivenBackups; + @Parameter(name = ApiConstants.DOMAIN_ID, + type = CommandType.STRING, + description = "the ID of the containing domain(s) as comma separated string, public for public offerings", + since = "4.23.0", + length = 4096) + private String domainIds; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -82,7 +94,7 @@ public Boolean getAllowUserDrivenBackups() { @Override public void execute() { try { - if (StringUtils.isAllEmpty(getName(), getDescription()) && getAllowUserDrivenBackups() == null) { + if (StringUtils.isAllEmpty(getName(), getDescription()) && getAllowUserDrivenBackups() == null && CollectionUtils.isEmpty(getDomainIds())) { throw new InvalidParameterValueException(String.format("Can't update Backup Offering [id: %s] because there are no parameters to be updated, at least one of the", "following should be informed: name, description or allowUserDrivenBackups.", id)); } @@ -98,11 +110,23 @@ public void execute() { this.setResponseObject(response); } catch (CloudRuntimeException e) { ApiErrorCode paramError = e instanceof InvalidParameterValueException ? ApiErrorCode.PARAM_ERROR : ApiErrorCode.INTERNAL_ERROR; - logger.error(String.format("Failed to update Backup Offering [id: %s] due to: [%s].", id, e.getMessage()), e); + logger.error("Failed to update Backup Offering [id: {}] due to: [{}].", id, e.getMessage(), e); throw new ServerApiException(paramError, e.getMessage()); } } + public List getDomainIds() { + // backupManager may be null in unit tests where the command is spied without injection. + // Avoid creating a method reference to a null receiver which causes NPE. When backupManager + // is null, pass null as the defaultDomainsProvider so resolveDomainIds will simply return + // an empty list or parse the explicit domainIds string. + LongFunction> defaultDomainsProvider = null; + if (backupManager != null) { + defaultDomainsProvider = backupManager::getBackupOfferingDomains; + } + return resolveDomainIds(domainIds, id, defaultDomainsProvider, "backup offering"); + } + @Override public long getEntityOwnerId() { return Account.ACCOUNT_ID_SYSTEM; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/ca/IssueCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/ca/IssueCertificateCmd.java index 463af000f58b..79dad4269c9b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/ca/IssueCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/ca/IssueCertificateCmd.java @@ -149,6 +149,6 @@ public String getEventType() { @Override public String getEventDescription() { - return "issuing certificate for domain(s)=" + domains + ", ip(s)=" + addresses; + return "Issuing certificate for domain(s)=" + domains + ", ip(s)=" + addresses; } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/ca/ProvisionCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/ca/ProvisionCertificateCmd.java index a39985549ac8..d333a74fdb3b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/ca/ProvisionCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/ca/ProvisionCertificateCmd.java @@ -52,7 +52,7 @@ public class ProvisionCertificateCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, required = true, entityType = HostResponse.class, - description = "The host/agent uuid to which the certificate has to be provisioned (issued and propagated)") + description = "The host/agent ID to which the certificate has to be provisioned (issued and propagated)") private Long hostId; @Parameter(name = ApiConstants.RECONNECT, type = CommandType.BOOLEAN, @@ -63,6 +63,12 @@ public class ProvisionCertificateCmd extends BaseAsyncCmd { description = "Name of the CA service provider, otherwise the default configured provider plugin will be used") private String provider; + @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, + description = "When true, uses SSH to re-provision the agent's certificate, bypassing the NIO agent connection. " + + "Use this when agents are disconnected due to a CA change. Supported for KVM hosts and SystemVMs. Default is false", + since = "4.23.0") + private Boolean forced; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -79,6 +85,10 @@ public String getProvider() { return provider; } + public boolean isForced() { + return forced != null && forced; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -90,7 +100,7 @@ public void execute() { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to find host by ID: " + getHostId()); } - boolean result = caManager.provisionCertificate(host, getReconnect(), getProvider()); + boolean result = caManager.provisionCertificate(host, getReconnect(), getProvider(), isForced()); SuccessResponse response = new SuccessResponse(getCommandName()); response.setSuccess(result); setResponseObject(response); @@ -108,7 +118,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "provisioning certificate for host id=" + hostId + " using provider=" + provider; + return "Provisioning certificate for host with ID: " + getResourceUuid(ApiConstants.HOST_ID) + " using provider: " + provider; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/ca/RevokeCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/ca/RevokeCertificateCmd.java index 381bed65f95c..c2212442f4ba 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/ca/RevokeCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/ca/RevokeCertificateCmd.java @@ -105,6 +105,6 @@ public String getEventType() { @Override public String getEventDescription() { - return "revoking certificate with serial id=" + serial + ", cn=" + cn; + return "Revoking certificate with serial id=" + serial + ", cn=" + cn; } } 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 3aef11b92e9d..d8fa2123d228 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 @@ -44,48 +44,48 @@ requestHasSensitiveInfo = true, responseHasSensitiveInfo = false) public class AddClusterCmd extends BaseCmd { - @Parameter(name = ApiConstants.CLUSTER_NAME, type = CommandType.STRING, required = true, description = "the cluster name") + @Parameter(name = ApiConstants.CLUSTER_NAME, type = CommandType.STRING, required = true, description = "The cluster name") private String clusterName; - @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, required = false, description = "the password for the host") + @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, required = false, description = "The password for the host") private String password; - @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, required = true, description = "the Pod ID for the host") + @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, required = true, description = "The Pod ID for the host") private Long podId; - @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = false, description = "the URL") + @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = false, description = "The URL") private String url; - @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, required = false, description = "the username for the cluster") + @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, required = false, description = "The username for the cluster") private String username; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "the Zone ID for the cluster") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "The Zone ID for the cluster") private Long zoneId; @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, required = true, - description = "hypervisor type of the cluster: XenServer,KVM,VMware,Hyperv,BareMetal,Simulator,Ovm3,External") + description = "Hypervisor type of the cluster: XenServer,KVM,VMware,Hyperv,BareMetal,Simulator,Ovm3,External") private String hypervisor; @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, - description = "the CPU arch of the cluster. Valid options are: x86_64, aarch64", + description = "The CPU arch of the cluster. Valid options are: x86_64, aarch64, s390x", since = "4.20") private String arch; - @Parameter(name = ApiConstants.CLUSTER_TYPE, type = CommandType.STRING, required = true, description = "type of the cluster: CloudManaged, ExternalManaged") + @Parameter(name = ApiConstants.CLUSTER_TYPE, type = CommandType.STRING, required = true, description = "Type of the cluster: CloudManaged, ExternalManaged") private String clusterType; @Parameter(name = ApiConstants.ALLOCATION_STATE, type = CommandType.STRING, description = "Allocation state of this cluster for allocation of new resources") private String allocationState; - @Parameter(name = ApiConstants.VSM_USERNAME, type = CommandType.STRING, required = false, description = "the username for the VSM associated with this cluster") + @Parameter(name = ApiConstants.VSM_USERNAME, type = CommandType.STRING, required = false, description = "The username for the VSM associated with this cluster") private String vsmusername; - @Parameter(name = ApiConstants.VSM_PASSWORD, type = CommandType.STRING, required = false, description = "the password for the VSM associated with this cluster") + @Parameter(name = ApiConstants.VSM_PASSWORD, type = CommandType.STRING, required = false, description = "The password for the VSM associated with this cluster") private String vsmpassword; - @Parameter(name = ApiConstants.VSM_IPADDRESS, type = CommandType.STRING, required = false, description = "the ipaddress of the VSM associated with this cluster") + @Parameter(name = ApiConstants.VSM_IPADDRESS, type = CommandType.STRING, required = false, description = "The IP address of the VSM associated with this cluster") private String vsmipaddress; @Parameter(name = ApiConstants.VSWITCH_TYPE_GUEST_TRAFFIC, @@ -109,7 +109,7 @@ public class AddClusterCmd extends BaseCmd { @Parameter(name = ApiConstants.VSWITCH_NAME_PUBLIC_TRAFFIC, type = CommandType.STRING, required = false, - description = "Name of virtual switch used for public traffic in the cluster. This would override zone wide traffic label setting.") + description = "Name of virtual switch used for public traffic in the cluster. This would override zone wide traffic label setting.") private String vSwitchNamePublicTraffic; @Parameter(name = ApiConstants.OVM3_POOL, type = CommandType.STRING, required = false, description = "Ovm3 native pooling enabled for cluster") diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/DeleteClusterCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/DeleteClusterCmd.java index 2b1cfe8bcb58..afabf7fef166 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/DeleteClusterCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/DeleteClusterCmd.java @@ -38,7 +38,7 @@ public class DeleteClusterCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ClusterResponse.class, required = true, description = "the cluster ID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ClusterResponse.class, required = true, description = "The cluster ID") private Long id; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/ExecuteClusterDrsPlanCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/ExecuteClusterDrsPlanCmd.java index 60f2c2b1deea..00e7da6e37c1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/ExecuteClusterDrsPlanCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/ExecuteClusterDrsPlanCmd.java @@ -142,6 +142,6 @@ public String getEventType() { @Override public String getEventDescription() { - return String.format("Executing DRS plan for cluster: %d", getId()); + return "Executing DRS plan for cluster with ID: " + getResourceUuid(ApiConstants.ID); } } 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 b38278e39469..9fe4e3f5cfc1 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 @@ -52,31 +52,31 @@ public class ListClustersCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "lists clusters by the cluster ID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "Lists clusters by the cluster ID") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "lists clusters by the cluster name") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Lists clusters by the cluster name") private String clusterName; - @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "lists clusters by Pod ID") + @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "Lists clusters by Pod ID") private Long podId; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "lists clusters by Zone ID") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "Lists clusters by Zone ID") private Long zoneId; - @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "lists clusters by hypervisor type") + @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "Lists clusters by hypervisor type") private String hypervisorType; - @Parameter(name = ApiConstants.CLUSTER_TYPE, type = CommandType.STRING, description = "lists clusters by cluster type") + @Parameter(name = ApiConstants.CLUSTER_TYPE, type = CommandType.STRING, description = "Lists clusters by cluster type") private String clusterType; - @Parameter(name = ApiConstants.ALLOCATION_STATE, type = CommandType.STRING, description = "lists clusters by allocation state") + @Parameter(name = ApiConstants.ALLOCATION_STATE, type = CommandType.STRING, description = "Lists clusters by allocation state") private String allocationState; - @Parameter(name = ApiConstants.MANAGED_STATE, type = CommandType.STRING, description = "whether this cluster is managed by cloudstack") + @Parameter(name = ApiConstants.MANAGED_STATE, type = CommandType.STRING, description = "Whether this cluster is managed by cloudstack") private String managedState; - @Parameter(name = ApiConstants.SHOW_CAPACITIES, type = CommandType.BOOLEAN, description = "flag to display the capacity of the clusters") + @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, 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 c160cfd2e034..77d0557af05b 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 @@ -39,26 +39,26 @@ public class UpdateClusterCmd extends BaseCmd { - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ClusterResponse.class, required = true, description = "the ID of the Cluster") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ClusterResponse.class, required = true, description = "The ID of the Cluster") private Long id; - @Parameter(name = ApiConstants.CLUSTER_NAME, type = CommandType.STRING, description = "the cluster name") + @Parameter(name = ApiConstants.CLUSTER_NAME, type = CommandType.STRING, description = "The cluster name") private String clusterName; - @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "hypervisor type of the cluster") + @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "Hypervisor type of the cluster") private String hypervisor; - @Parameter(name = ApiConstants.CLUSTER_TYPE, type = CommandType.STRING, description = "hypervisor type of the cluster") + @Parameter(name = ApiConstants.CLUSTER_TYPE, type = CommandType.STRING, description = "Hypervisor type of the cluster") private String clusterType; @Parameter(name = ApiConstants.ALLOCATION_STATE, type = CommandType.STRING, description = "Allocation state of this cluster for allocation of new resources") private String allocationState; - @Parameter(name = ApiConstants.MANAGED_STATE, type = CommandType.STRING, description = "whether this cluster is managed by cloudstack") + @Parameter(name = ApiConstants.MANAGED_STATE, type = CommandType.STRING, description = "Whether this cluster is managed by cloudstack") private String managedState; @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, - description = "the CPU arch of the cluster. Valid options are: x86_64, aarch64", + description = "the CPU arch of the cluster. Valid options are: x86_64, aarch64, s390x", since = "4.20") private String arch; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ListCfgGroupsByCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ListCfgGroupsByCmd.java index d735218169d6..c63e7cf92d4d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ListCfgGroupsByCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ListCfgGroupsByCmd.java @@ -40,7 +40,7 @@ public class ListCfgGroupsByCmd extends BaseListCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.GROUP, type = CommandType.STRING, description = "lists configuration group by group name") + @Parameter(name = ApiConstants.GROUP, type = CommandType.STRING, description = "Lists configuration group by group name") private String groupName; // /////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ListCfgsByCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ListCfgsByCmd.java index e365d8bc2dc7..055443934609 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ListCfgsByCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ListCfgsByCmd.java @@ -19,23 +19,23 @@ import java.util.ArrayList; import java.util.List; -import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.response.DomainResponse; -import org.apache.commons.lang3.StringUtils; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseListCmd; import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.AccountResponse; import org.apache.cloudstack.api.response.ClusterResponse; import org.apache.cloudstack.api.response.ConfigurationResponse; +import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.ImageStoreResponse; import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ManagementServerResponse; import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.config.Configuration; +import org.apache.commons.lang3.StringUtils; import com.cloud.exception.InvalidParameterValueException; import com.cloud.utils.Pair; @@ -52,55 +52,62 @@ public class ListCfgsByCmd extends BaseListCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.CATEGORY, type = CommandType.STRING, description = "lists configurations by category") + @Parameter(name = ApiConstants.CATEGORY, type = CommandType.STRING, description = "Lists configurations by category") private String category; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "lists configuration by name") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Lists configuration by name") private String configName; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, - description = "the ID of the Zone to update the parameter value for corresponding zone") + description = "The ID of the Zone to update the parameter value for corresponding zone") private Long zoneId; @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, - description = "the ID of the Cluster to update the parameter value for corresponding cluster") + description = "The ID of the Cluster to update the parameter value for corresponding cluster") private Long clusterId; @Parameter(name = ApiConstants.STORAGE_ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, - description = "the ID of the Storage pool to update the parameter value for corresponding storage pool") + description = "The ID of the Storage pool to update the parameter value for corresponding storage pool") private Long storagePoolId; @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, - description = "the ID of the Account to update the parameter value for corresponding account") + description = "The ID of the Account to update the parameter value for corresponding account") private Long accountId; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "the ID of the Domain to update the parameter value for corresponding domain") + description = "The ID of the Domain to update the parameter value for corresponding domain") private Long domainId; @Parameter(name = ApiConstants.IMAGE_STORE_UUID, type = CommandType.UUID, entityType = ImageStoreResponse.class, - description = "the ID of the Image Store to update the parameter value for corresponding image store") + description = "The ID of the Image Store to update the parameter value for corresponding image store") private Long imageStoreId; - @Parameter(name = ApiConstants.GROUP, type = CommandType.STRING, description = "lists configuration by group name (primarily used for UI)", since = "4.18.0") + @Parameter(name = ApiConstants.MANAGEMENT_SERVER_ID, + type = CommandType.UUID, + entityType = ManagementServerResponse.class, + description = "the ID of the Management Server to update the parameter value for corresponding management server", + since = "4.23.0") + private Long managementServerId; + + @Parameter(name = ApiConstants.GROUP, type = CommandType.STRING, description = "Lists configuration by group name (primarily used for UI)", since = "4.18.0") private String groupName; - @Parameter(name = ApiConstants.SUBGROUP, type = CommandType.STRING, description = "lists configuration by subgroup name (primarily used for UI)", since = "4.18.0") + @Parameter(name = ApiConstants.SUBGROUP, type = CommandType.STRING, description = "Lists configuration by subgroup name (primarily used for UI)", since = "4.18.0") private String subGroupName; - @Parameter(name = ApiConstants.PARENT, type = CommandType.STRING, description = "lists configuration by parent name (primarily used for UI)", since = "4.18.0") + @Parameter(name = ApiConstants.PARENT, type = CommandType.STRING, description = "Lists configuration by parent name (primarily used for UI)", since = "4.18.0") private String parentName; // /////////////////////////////////////////////////// @@ -139,6 +146,10 @@ public Long getImageStoreId() { return imageStoreId; } + public Long getManagementServerId() { + return managementServerId; + } + public String getGroupName() { return groupName; } @@ -200,6 +211,9 @@ private void setScope(ConfigurationResponse cfgResponse) { if (getImageStoreId() != null){ cfgResponse.setScope("imagestore"); } + if (getManagementServerId() != null){ + cfgResponse.setScope("managementserver"); + } } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ListHypervisorCapabilitiesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ListHypervisorCapabilitiesCmd.java index e7cc9e0234e2..d8622f94b6b6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ListHypervisorCapabilitiesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ListHypervisorCapabilitiesCmd.java @@ -47,7 +47,7 @@ public class ListHypervisorCapabilitiesCmd extends BaseListCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HypervisorCapabilitiesResponse.class, description = "ID of the hypervisor capability") private Long id; - @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "the hypervisor for which to restrict the search") + @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "The hypervisor for which to restrict the search") private String hypervisor; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ResetCfgCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ResetCfgCmd.java index f114b263b634..3c3c36b29d7e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ResetCfgCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ResetCfgCmd.java @@ -23,16 +23,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.ImageStoreResponse; -import org.apache.cloudstack.framework.config.ConfigKey; - import org.apache.cloudstack.api.response.AccountResponse; import org.apache.cloudstack.api.response.ClusterResponse; import org.apache.cloudstack.api.response.ConfigurationResponse; import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ImageStoreResponse; +import org.apache.cloudstack.api.response.ManagementServerResponse; import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.config.Configuration; +import org.apache.cloudstack.framework.config.ConfigKey; import com.cloud.user.Account; import com.cloud.utils.Pair; @@ -45,45 +45,52 @@ public class ResetCfgCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the configuration", validations = {ApiArgValidator.NotNullOrEmpty}) + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "The name of the configuration", validations = {ApiArgValidator.NotNullOrEmpty}) private String cfgName; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, - description = "the ID of the Zone to reset the parameter value for corresponding zone") + description = "The ID of the Zone to reset the parameter value for corresponding zone") private Long zoneId; @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, - description = "the ID of the Cluster to reset the parameter value for corresponding cluster") + description = "The ID of the Cluster to reset the parameter value for corresponding cluster") private Long clusterId; @Parameter(name = ApiConstants.STORAGE_ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, - description = "the ID of the Storage pool to reset the parameter value for corresponding storage pool") + description = "The ID of the Storage pool to reset the parameter value for corresponding storage pool") private Long storagePoolId; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "the ID of the Domain to reset the parameter value for corresponding domain") + description = "The ID of the Domain to reset the parameter value for corresponding domain") private Long domainId; @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, - description = "the ID of the Account to reset the parameter value for corresponding account") + description = "The ID of the Account to reset the parameter value for corresponding account") private Long accountId; @Parameter(name = ApiConstants.IMAGE_STORE_ID, type = CommandType.UUID, entityType = ImageStoreResponse.class, - description = "the ID of the Image Store to reset the parameter value for corresponding image store") + description = "The ID of the Image Store to reset the parameter value for corresponding image store") private Long imageStoreId; + @Parameter(name = ApiConstants.MANAGEMENT_SERVER_ID, + type = CommandType.UUID, + entityType = ManagementServerResponse.class, + description = "the ID of the Management Server to update the parameter value for corresponding management server", + since = "4.23.0") + private Long managementServerId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -116,6 +123,10 @@ public Long getImageStoreId() { return imageStoreId; } + public Long getManagementServerId() { + return managementServerId; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -149,6 +160,9 @@ public void execute() { if (getImageStoreId() != null) { response.setScope(ConfigKey.Scope.ImageStore.name()); } + if (getManagementServerId() != null) { + response.setScope(ConfigKey.Scope.ManagementServer.name()); + } response.setValue(cfg.second()); this.setResponseObject(response); } else { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/config/UpdateCfgCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/config/UpdateCfgCmd.java index dbf478df7012..9db9529dc8d8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/config/UpdateCfgCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/config/UpdateCfgCmd.java @@ -16,9 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.admin.config; -import com.cloud.utils.crypt.DBEncryptionUtil; import org.apache.cloudstack.acl.RoleService; -import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiArgValidator; import org.apache.cloudstack.api.ApiConstants; @@ -29,13 +27,17 @@ import org.apache.cloudstack.api.response.AccountResponse; import org.apache.cloudstack.api.response.ClusterResponse; import org.apache.cloudstack.api.response.ConfigurationResponse; +import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.ImageStoreResponse; +import org.apache.cloudstack.api.response.ManagementServerResponse; import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.config.Configuration; +import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.commons.lang3.StringUtils; import com.cloud.user.Account; +import com.cloud.utils.crypt.DBEncryptionUtil; @APICommand(name = "updateConfiguration", description = "Updates a configuration.", responseObject = ConfigurationResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -45,49 +47,56 @@ public class UpdateCfgCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the configuration") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "The name of the configuration") private String cfgName; - @Parameter(name = ApiConstants.VALUE, type = CommandType.STRING, description = "the value of the configuration", length = 4096) + @Parameter(name = ApiConstants.VALUE, type = CommandType.STRING, description = "The value of the configuration", length = 4096) private String value; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, - description = "the ID of the Zone to update the parameter value for corresponding zone") + description = "The ID of the Zone to update the parameter value for corresponding zone") private Long zoneId; @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, - description = "the ID of the Cluster to update the parameter value for corresponding cluster") + description = "The ID of the Cluster to update the parameter value for corresponding cluster") private Long clusterId; @Parameter(name = ApiConstants.STORAGE_ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, - description = "the ID of the Storage pool to update the parameter value for corresponding storage pool") + description = "The ID of the Storage pool to update the parameter value for corresponding storage pool") private Long storagePoolId; @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, - description = "the ID of the Account to update the parameter value for corresponding account") + description = "The ID of the Account to update the parameter value for corresponding account") private Long accountId; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "the ID of the Domain to update the parameter value for corresponding domain") + description = "The ID of the Domain to update the parameter value for corresponding domain") private Long domainId; @Parameter(name = ApiConstants.IMAGE_STORE_UUID, type = CommandType.UUID, entityType = ImageStoreResponse.class, - description = "the ID of the Image Store to update the parameter value for corresponding image store", + description = "The ID of the Image Store to update the parameter value for corresponding image store", validations = ApiArgValidator.PositiveNumber) private Long imageStoreId; + @Parameter(name = ApiConstants.MANAGEMENT_SERVER_ID, + type = CommandType.UUID, + entityType = ManagementServerResponse.class, + description = "the ID of the Management Server to update the parameter value for corresponding management server", + since = "4.23.0") + private Long managementServerId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -112,7 +121,7 @@ public Long getClusterId() { return clusterId; } - public Long getStoragepoolId() { + public Long getStoragePoolId() { return storagePoolId; } @@ -128,6 +137,10 @@ public Long getImageStoreId() { return imageStoreId; } + public Long getManagementServerId() { + return managementServerId; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -150,7 +163,7 @@ public void execute() { ConfigurationResponse response = _responseGenerator.createConfigurationResponse(cfg); response.setResponseName(getCommandName()); response = setResponseScopes(response); - response = setResponseValue(response, cfg); + setResponseValue(response, cfg); this.setResponseObject(response); } else { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update config"); @@ -161,15 +174,13 @@ public void execute() { * Sets the configuration value in the response. If the configuration is in the `Hidden` or `Secure` categories, the value is encrypted before being set in the response. * @param response to be set with the configuration `cfg` value * @param cfg to be used in setting the response value - * @return the response with the configuration's value */ - public ConfigurationResponse setResponseValue(ConfigurationResponse response, Configuration cfg) { + public void setResponseValue(ConfigurationResponse response, Configuration cfg) { + String value = cfg.getValue(); if (cfg.isEncrypted()) { - response.setValue(DBEncryptionUtil.encrypt(getValue())); - } else { - response.setValue(getValue()); + value = DBEncryptionUtil.encrypt(value); } - return response; + response.setValue(value); } /** @@ -184,7 +195,7 @@ public ConfigurationResponse setResponseScopes(ConfigurationResponse response) { if (getClusterId() != null) { response.setScope("cluster"); } - if (getStoragepoolId() != null) { + if (getStoragePoolId() != null) { response.setScope("storagepool"); } if (getAccountId() != null) { @@ -193,6 +204,9 @@ public ConfigurationResponse setResponseScopes(ConfigurationResponse response) { if (getDomainId() != null) { response.setScope("domain"); } + if (getManagementServerId() != null) { + response.setScope(ConfigKey.Scope.ManagementServer.name().toLowerCase()); + } return response; } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/config/UpdateHypervisorCapabilitiesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/config/UpdateHypervisorCapabilitiesCmd.java index 01f7af108416..d7342654ec40 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/config/UpdateHypervisorCapabilitiesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/config/UpdateHypervisorCapabilitiesCmd.java @@ -43,28 +43,28 @@ public class UpdateHypervisorCapabilitiesCmd extends BaseCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HypervisorCapabilitiesResponse.class, description = "ID of the hypervisor capability") private Long id; - @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "the hypervisor for which the hypervisor capabilities are to be updated", since = "4.19.1") + @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "The hypervisor for which the hypervisor capabilities are to be updated", since = "4.19.1") private String hypervisor; - @Parameter(name = ApiConstants.HYPERVISOR_VERSION, type = CommandType.STRING, description = "the hypervisor version for which the hypervisor capabilities are to be updated", since = "4.19.1") + @Parameter(name = ApiConstants.HYPERVISOR_VERSION, type = CommandType.STRING, description = "The hypervisor version for which the hypervisor capabilities are to be updated", since = "4.19.1") private String hypervisorVersion; - @Parameter(name = ApiConstants.SECURITY_GROUP_EANBLED, type = CommandType.BOOLEAN, description = "set true to enable security group for this hypervisor.") + @Parameter(name = ApiConstants.SECURITY_GROUP_EANBLED, type = CommandType.BOOLEAN, description = "Set true to enable security group for this hypervisor.") private Boolean securityGroupEnabled; - @Parameter(name = ApiConstants.MAX_GUESTS_LIMIT, type = CommandType.LONG, description = "the max number of Guest VMs per host for this hypervisor.") + @Parameter(name = ApiConstants.MAX_GUESTS_LIMIT, type = CommandType.LONG, description = "The maximum number of Guest Instances per host for this hypervisor.") private Long maxGuestsLimit; - @Parameter(name = ApiConstants.MAX_DATA_VOLUMES_LIMIT, type = CommandType.INTEGER, description = "the maximum number of Data Volumes that can be attached to a VM for this hypervisor.", since = "4.16.0") + @Parameter(name = ApiConstants.MAX_DATA_VOLUMES_LIMIT, type = CommandType.INTEGER, description = "The maximum number of Data Volumes that can be attached to an Instance for this hypervisor.", since = "4.16.0") private Integer maxDataVolumesLimit; - @Parameter(name = ApiConstants.STORAGE_MOTION_ENABLED, type = CommandType.BOOLEAN, description = "set true to enable storage motion support for this hypervisor", since = "4.16.0") + @Parameter(name = ApiConstants.STORAGE_MOTION_ENABLED, type = CommandType.BOOLEAN, description = "Set true to enable storage motion support for this hypervisor", since = "4.16.0") private Boolean storageMotionSupported; - @Parameter(name = ApiConstants.MAX_HOSTS_PER_CLUSTER, type = CommandType.INTEGER, description = "the maximum number of the hypervisor hosts per cluster ", since = "4.16.0") + @Parameter(name = ApiConstants.MAX_HOSTS_PER_CLUSTER, type = CommandType.INTEGER, description = "The maximum number of the hypervisor hosts per cluster ", since = "4.16.0") private Integer maxHostsPerClusterLimit; - @Parameter(name = ApiConstants.VM_SNAPSHOT_ENABELD, type = CommandType.BOOLEAN, description = "set true to enable VM snapshots for this hypervisor", since = "4.16.0") + @Parameter(name = ApiConstants.VM_SNAPSHOT_ENABELD, type = CommandType.BOOLEAN, description = "Set true to enable Instance Snapshots for this hypervisor", since = "4.16.0") private Boolean vmSnapshotEnabled; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/diagnostics/GetDiagnosticsDataCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/diagnostics/GetDiagnosticsDataCmd.java index 553ba6b1729d..c140de5aa01e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/diagnostics/GetDiagnosticsDataCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/diagnostics/GetDiagnosticsDataCmd.java @@ -48,7 +48,7 @@ entityType = {VirtualMachine.class}, responseHasSensitiveInfo = false, requestHasSensitiveInfo = false, - description = "Get diagnostics and files from system VMs", + description = "Get diagnostics and files from System VMs", since = "4.14.0.0", authorized = {RoleType.Admin}) public class GetDiagnosticsDataCmd extends BaseAsyncCmd { @@ -64,7 +64,7 @@ public class GetDiagnosticsDataCmd extends BaseAsyncCmd { entityType = SystemVmResponse.class, required = true, validations = {ApiArgValidator.PositiveNumber}, - description = "The ID of the system VM instance to retrieve diagnostics data files from") + description = "The ID of the System VM to retrieve diagnostics data files from") private Long id; @Parameter(name = ApiConstants.FILES, @@ -113,7 +113,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE response.setResponseName(getCommandName()); this.setResponseObject(response); } else { - throw new CloudRuntimeException("failed to generate valid download url: " + downloadUrl); + throw new CloudRuntimeException("Failed to generate valid download url: " + downloadUrl); } } catch (ServerApiException e) { throw new CloudRuntimeException("Internal exception caught while retrieving diagnostics files: ", e); @@ -140,7 +140,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Getting diagnostics data files from system vm: " + this._uuidMgr.getUuid(VirtualMachine.class, getId()); + return "Getting diagnostics data files from System Instance with ID: " + getResourceUuid(ApiConstants.TARGET_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/diagnostics/RunDiagnosticsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/diagnostics/RunDiagnosticsCmd.java index 4537eb6f215c..d1f22baf6604 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/diagnostics/RunDiagnosticsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/diagnostics/RunDiagnosticsCmd.java @@ -48,7 +48,7 @@ @APICommand(name = "runDiagnostics", responseObject = RunDiagnosticsResponse.class, entityType = {VirtualMachine.class}, responseHasSensitiveInfo = false, requestHasSensitiveInfo = false, - description = "Execute network-utility command (ping/arping/tracert) on system VMs remotely", + description = "Execute network-utility command (ping/arping/tracert) on System VMs remotely", authorized = {RoleType.Admin}, since = "4.12.0.0") public class RunDiagnosticsCmd extends BaseAsyncCmd { @@ -62,7 +62,7 @@ public class RunDiagnosticsCmd extends BaseAsyncCmd { @ACL(accessType = SecurityChecker.AccessType.OperateEntry) @Parameter(name = ApiConstants.TARGET_ID, type = CommandType.UUID, required = true, entityType = SystemVmResponse.class, validations = {ApiArgValidator.PositiveNumber}, - description = "The ID of the system VM instance to diagnose") + description = "The ID of the System VM to diagnose") private Long id; @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, required = true, @@ -70,7 +70,7 @@ public class RunDiagnosticsCmd extends BaseAsyncCmd { private String address; @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, required = true, - description = "The system VM diagnostics type valid options are: ping, traceroute, arping") + description = "The System VM diagnostics type valid options are: ping, traceroute, arping") private String type; @Parameter(name = ApiConstants.PARAMS, type = CommandType.STRING, @@ -153,7 +153,7 @@ public ApiCommandResourceType getApiResourceType() { @Override public String getEventDescription() { - return "Executing diagnostics on system vm: " + this._uuidMgr.getUuid(VirtualMachine.class, getId()); + return "Executing diagnostics on System Instance with ID: " + getResourceUuid(ApiConstants.TARGET_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java index 145ff6ba7823..9a605ec4200d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java @@ -41,7 +41,7 @@ import java.util.List; @APICommand(name = "listTemplateDirectDownloadCertificates", - description = "List the uploaded certificates for direct download templates", + description = "List the uploaded certificates for direct download Templates", responseObject = DirectDownloadCertificateResponse.class, since = "4.17.0", authorized = {RoleType.Admin}) @@ -51,15 +51,15 @@ public class ListTemplateDirectDownloadCertificatesCmd extends BaseListCmd { DirectDownloadManager directDownloadManager; @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DirectDownloadCertificateResponse.class, - description = "list direct download certificate by ID") + description = "List direct download certificate by ID") private Long id; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, - description = "the zone where certificates are uploaded") + description = "The zone where certificates are uploaded") private Long zoneId; @Parameter(name = ApiConstants.LIST_HOSTS, type = CommandType.BOOLEAN, - description = "if set to true: include the hosts where the certificate is uploaded to") + description = "If set to true: include the hosts where the certificate is uploaded to") private Boolean listHosts; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ProvisionTemplateDirectDownloadCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ProvisionTemplateDirectDownloadCertificateCmd.java index 88f538547e10..3dfbbd940c40 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ProvisionTemplateDirectDownloadCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ProvisionTemplateDirectDownloadCertificateCmd.java @@ -50,11 +50,11 @@ public class ProvisionTemplateDirectDownloadCertificateCmd extends BaseCmd { DirectDownloadManager directDownloadManager; @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DirectDownloadCertificateResponse.class, - description = "the id of the direct download certificate to provision", required = true) + description = "The id of the direct download certificate to provision", required = true) private Long id; @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, - description = "the host to provision the certificate", required = true) + description = "The host to provision the certificate", required = true) private Long hostId; @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java index eb9031cbc587..1ad8f271cfcf 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java @@ -59,23 +59,23 @@ public class RevokeTemplateDirectDownloadCertificateCmd extends BaseCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DirectDownloadCertificateResponse.class, - description = "id of the certificate") + description = "ID of the certificate") private Long certificateId; @Parameter(name = ApiConstants.NAME, type = BaseCmd.CommandType.STRING, - description = "(optional) alias of the SSL certificate") + description = "(Optional) alias of the SSL certificate") private String certificateAlias; @Parameter(name = ApiConstants.HYPERVISOR, type = BaseCmd.CommandType.STRING, - description = "(optional) hypervisor type") + description = "(Optional) hypervisor type") private String hypervisor; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, - description = "(optional) zone to revoke certificate", required = true) + description = "(Optional) zone to revoke certificate", required = true) private Long zoneId; @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, - description = "(optional) the host ID to revoke certificate") + description = "(Optional) the host ID to revoke certificate") private Long hostId; private void createResponse(final List hostsRevokeStatusList) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java index c5c102be56d6..ad440376a913 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java @@ -39,7 +39,7 @@ import java.util.List; @APICommand(name = "uploadTemplateDirectDownloadCertificate", - description = "Upload a certificate for HTTPS direct template download on KVM hosts", + description = "Upload a certificate for HTTPS direct Template download on KVM hosts", responseObject = DirectDownloadCertificateResponse.class, since = "4.11.0", authorized = {RoleType.Admin}) @@ -65,7 +65,7 @@ public class UploadTemplateDirectDownloadCertificateCmd extends BaseCmd { private Long zoneId; @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, - description = "(optional) the host ID to upload certificate") + description = "(Optional) the host ID to upload certificate") private Long hostId; private void createResponse(DirectDownloadCertificate certificate, final List hostStatusList) { @@ -95,7 +95,7 @@ public void execute() { } try { - logger.debug("Uploading certificate " + name + " to agents for Direct Download"); + logger.debug("Uploading certificate {} to agents for Direct Download", name); Pair> uploadStatus = directDownloadManager.uploadCertificateToHosts(certificate, name, hypervisor, zoneId, hostId); DirectDownloadCertificate certificate = uploadStatus.first(); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/CreateDomainCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/CreateDomainCmd.java index c7f06920bb8d..d2775548a841 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/CreateDomainCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/CreateDomainCmd.java @@ -40,13 +40,13 @@ public class CreateDomainCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "creates domain with this name") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Creates domain with this name") private String domainName; @Parameter(name = ApiConstants.PARENT_DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "assigns new domain a parent domain by domain ID of the parent. If no parent domain is specified, the ROOT domain is assumed.") + description = "Assigns new domain a parent domain by domain ID of the parent. If no parent domain is specified, the ROOT domain is assumed.") private Long parentDomainId; @Parameter(name = ApiConstants.NETWORK_DOMAIN, type = CommandType.STRING, description = "Network domain for networks in the domain") @@ -86,7 +86,7 @@ public long getEntityOwnerId() { @Override public void execute() { - CallContext.current().setEventDetails("Domain Name: " + getDomainName() + ((getParentDomainId() != null) ? ", Parent DomainId :" + getParentDomainId() : "")); + CallContext.current().setEventDetails("Domain Name: " + getDomainName() + ((getParentDomainId() != null) ? ", Parent Domain ID:" + getResourceUuid(ApiConstants.PARENT_DOMAIN_ID) : "")); Domain domain = _domainService.createDomain(getDomainName(), getParentDomainId(), getNetworkDomain(), getDomainUUID()); if (domain != null) { DomainResponse response = _responseGenerator.createDomainResponse(domain); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/DeleteDomainCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/DeleteDomainCmd.java index db3bae25e399..cf02e6a56bf8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/DeleteDomainCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/DeleteDomainCmd.java @@ -49,7 +49,7 @@ public class DeleteDomainCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.CLEANUP, type = CommandType.BOOLEAN, - description = "true if all domain resources (child domains, accounts) have to be cleaned up, false otherwise") + description = "True if all domain resources (child domains, Accounts) have to be cleaned up, false otherwise") private Boolean cleanup; @Inject @@ -88,12 +88,12 @@ public String getEventType() { @Override public String getEventDescription() { - return "deleting domain: " + getId(); + return "Deleting domain with ID: " + getResourceUuid(ApiConstants.ID); } @Override public void execute() { - CallContext.current().setEventDetails("Domain Id: " + getId()); + CallContext.current().setEventDetails("Domain ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _regionService.deleteDomain(this); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/ListDomainChildrenCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/ListDomainChildrenCmd.java index 8514bb6dda56..e93948987bb7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/ListDomainChildrenCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/ListDomainChildrenCmd.java @@ -42,15 +42,15 @@ public class ListDomainChildrenCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "list children domain by parent domain ID.") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "List children domain by parent domain ID.") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "list children domains by name") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "List children domains by name") private String domainName; @Parameter(name = ApiConstants.IS_RECURSIVE, type = CommandType.BOOLEAN, - description = "to return the entire tree, use the value \"true\". To return the first level children, use the value \"false\".") + description = "To return the entire tree, use the value \"true\". To return the first level children, use the value \"false\".") private Boolean recursive; @Parameter(name = ApiConstants.LIST_ALL, @@ -59,7 +59,7 @@ public class ListDomainChildrenCmd extends BaseListCmd { private Boolean listAll; @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, - description = "flag to display the resource icon for domains") + description = "Flag to display the resource icon for domains") private Boolean showIcon; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/ListDomainsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/ListDomainsCmd.java index 895e93289923..aa1978042265 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/ListDomainsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/ListDomainsCmd.java @@ -23,7 +23,7 @@ import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiConstants.DomainDetails; -import org.apache.cloudstack.api.BaseListCmd; +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; @@ -39,7 +39,7 @@ @APICommand(name = "listDomains", description = "Lists domains and provides detailed information for listed domains", responseObject = DomainResponse.class, responseView = ResponseView.Restricted, entityType = {Domain.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class ListDomainsCmd extends BaseListCmd implements UserCmd { +public class ListDomainsCmd extends BaseListTaggedResourcesCmd implements UserCmd { private static final String s_name = "listdomainsresponse"; @@ -64,11 +64,11 @@ public class ListDomainsCmd extends BaseListCmd implements UserCmd { @Parameter(name = ApiConstants.DETAILS, type = CommandType.LIST, collectionType = CommandType.STRING, - description = "comma separated list of domain details requested, value can be a list of [ all, resource, min]") + description = "Comma separated list of domain details requested, value can be a list of [ all, resource, min]") private List viewDetails; @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, - description = "flag to display the resource icon for domains") + description = "Flag to display the resource icon for domains") private Boolean showIcon; @Parameter(name = ApiConstants.TAG, type = CommandType.STRING, description = "Tag for resource type to return usage", since = "4.20.0") diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/UpdateDomainCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/UpdateDomainCmd.java index 353cb852bfdf..124a84931548 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/UpdateDomainCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/UpdateDomainCmd.java @@ -44,7 +44,7 @@ public class UpdateDomainCmd extends BaseCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DomainResponse.class, required = true, description = "ID of domain to update") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "updates domain with this name") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Updates domain with this name") private String domainName; @Parameter(name = ApiConstants.NETWORK_DOMAIN, @@ -82,7 +82,7 @@ public long getEntityOwnerId() { @Override public void execute() { - CallContext.current().setEventDetails("Domain Id: " + getId()); + CallContext.current().setEventDetails("Domain ID: " + getResourceUuid(ApiConstants.ID)); Domain domain = _regionService.updateDomain(this); if (domain != null) { 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 index 2ac07a9fb3a0..83aca1a2eb64 100644 --- 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 @@ -46,7 +46,7 @@ public class DiscoverGpuDevicesCmd extends BaseListCmd { @Override public void execute() { - CallContext.current().setEventDetails("Discovering GPU Devices on host id: " + getId()); + CallContext.current().setEventDetails("Discovering GPU Devices on host with ID: " + getResourceUuid(ApiConstants.ID)); ListResponse response = gpuService.discoverGpuDevices(this); response.setResponseName(getCommandName()); setResponseObject(response); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCmd.java index b854e8389c4f..c0e995c497d2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCmd.java @@ -120,7 +120,7 @@ public void create() { @Override public void execute() { - CallContext.current().setEventDetails("Guest OS Id: " + getEntityId()); + CallContext.current().setEventDetails("Guest OS ID: " + getEntityUuid()); GuestOS guestOs = _mgr.getAddedGuestOs(getEntityId()); if (guestOs != null) { GuestOSResponse response = _responseGenerator.createGuestOSResponse(guestOs); @@ -138,7 +138,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "adding a new guest OS type Id: " + getEntityId(); + return "Adding a new guest OS type Id: " + getEntityId(); } @Override @@ -153,7 +153,7 @@ public String getCreateEventType() { @Override public String getCreateEventDescription() { - return "adding new guest OS type"; + return "Adding new guest OS type"; } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsMappingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsMappingCmd.java index 3fdfebb54bf5..1a32168308ed 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsMappingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsMappingCmd.java @@ -133,7 +133,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "adding a new guest OS mapping Id: " + getEntityId(); + return "Adding a new guest OS mapping Id: " + getEntityId(); } @Override @@ -148,6 +148,6 @@ public String getCreateEventType() { @Override public String getCreateEventDescription() { - return "adding new guest OS mapping"; + return "Adding new guest OS mapping"; } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/GetHypervisorGuestOsNamesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/GetHypervisorGuestOsNamesCmd.java index da920a2ec2d0..811ecc8b165b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/GetHypervisorGuestOsNamesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/GetHypervisorGuestOsNamesCmd.java @@ -44,11 +44,11 @@ public class GetHypervisorGuestOsNamesCmd extends BaseAsyncCmd { validations = {ApiArgValidator.NotNullOrEmpty}) private String hypervisor; - @Parameter(name = ApiConstants.HYPERVISOR_VERSION, type = CommandType.STRING, required = true, description = "Hypervisor version to get the guest os names (atleast one hypervisor host with the version specified must be available)", + @Parameter(name = ApiConstants.HYPERVISOR_VERSION, type = CommandType.STRING, required = true, description = "Hypervisor version to get the guest OS names (at least one hypervisor host with the version specified must be available)", validations = {ApiArgValidator.NotNullOrEmpty}) private String hypervisorVersion; - @Parameter(name = ApiConstants.KEYWORD, type = CommandType.STRING, required = false, description = "Keyword for guest os name") + @Parameter(name = ApiConstants.KEYWORD, type = CommandType.STRING, required = false, description = "Keyword for guest OS name") private String keyword; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/ListGuestOsMappingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/ListGuestOsMappingCmd.java index 23e62cdc7810..47082dae68c2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/ListGuestOsMappingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/ListGuestOsMappingCmd.java @@ -40,22 +40,22 @@ public class ListGuestOsMappingCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOsMappingResponse.class, required = false, description = "list mapping by its UUID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOsMappingResponse.class, required = false, description = "List mapping by its UUID") private Long id; - @Parameter(name = ApiConstants.OS_TYPE_ID, type = CommandType.UUID, entityType = GuestOSResponse.class, required = false, description = "list mapping by Guest OS Type UUID") + @Parameter(name = ApiConstants.OS_TYPE_ID, type = CommandType.UUID, entityType = GuestOSResponse.class, required = false, description = "List mapping by Guest OS Type UUID") private Long osTypeId; - @Parameter(name = ApiConstants.OS_DISPLAY_NAME, type = CommandType.STRING, required = false, description = "list Guest OS mapping by OS display name") + @Parameter(name = ApiConstants.OS_DISPLAY_NAME, type = CommandType.STRING, required = false, description = "List Guest OS mapping by OS display name") private String osDisplayName; - @Parameter(name = ApiConstants.OS_NAME_FOR_HYPERVISOR, type = CommandType.STRING, required = false, description = "list Guest OS mapping by OS mapping name with hypervisor") + @Parameter(name = ApiConstants.OS_NAME_FOR_HYPERVISOR, type = CommandType.STRING, required = false, description = "List Guest OS mapping by OS mapping name with hypervisor") private String osNameForHypervisor; - @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, required = false, description = "list Guest OS mapping by hypervisor") + @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, required = false, description = "List Guest OS mapping by hypervisor") private String hypervisor; - @Parameter(name = ApiConstants.HYPERVISOR_VERSION, type = CommandType.STRING, required = false, description = "list Guest OS mapping by hypervisor version. Must be used with hypervisor parameter") + @Parameter(name = ApiConstants.HYPERVISOR_VERSION, type = CommandType.STRING, required = false, description = "List Guest OS mapping by hypervisor version. Must be used with hypervisor parameter") private String hypervisorVersion; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/RemoveGuestOsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/RemoveGuestOsCmd.java index d38682ce5bb4..f5c7d965c13f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/RemoveGuestOsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/RemoveGuestOsCmd.java @@ -62,7 +62,7 @@ public long getEntityOwnerId() { @Override public void execute() { - CallContext.current().setEventDetails("Guest OS Id: " + id); + CallContext.current().setEventDetails("Guest OS ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _mgr.removeGuestOs(this); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); @@ -74,7 +74,7 @@ public void execute() { @Override public String getEventDescription() { - return "Removing Guest OS: " + getId(); + return "Removing Guest OS with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/RemoveGuestOsMappingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/RemoveGuestOsMappingCmd.java index a472ab672c55..bd4a53889f25 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/RemoveGuestOsMappingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/RemoveGuestOsMappingCmd.java @@ -62,7 +62,7 @@ public long getEntityOwnerId() { @Override public void execute() { - CallContext.current().setEventDetails("Guest OS Mapping Id: " + id); + CallContext.current().setEventDetails("Guest OS Mapping ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _mgr.removeGuestOsMapping(this); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); @@ -74,7 +74,7 @@ public void execute() { @Override public String getEventDescription() { - return "Removing Guest OS Mapping: " + getId(); + return "Removing Guest OS Mapping with ID: " + getResourceUuid(ApiConstants.ID); } @Override 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 59909e09854a..035ff6a19e24 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 @@ -123,7 +123,7 @@ public void execute() { @Override public String getEventDescription() { - return "Updating guest OS: " + getId(); + return "Updating guest OS with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsMappingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsMappingCmd.java index fc67ef0a7e76..161bb5323070 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsMappingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsMappingCmd.java @@ -86,7 +86,7 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - return "Updating Guest OS Mapping: " + getId(); + return "Updating Guest OS with ID: " + getResourceUuid(ApiConstants.ID) + " mapping."; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/ConfigureHAForHostCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/ConfigureHAForHostCmd.java index 12033c04b80f..cb427e659495 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/ConfigureHAForHostCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/ConfigureHAForHostCmd.java @@ -87,6 +87,7 @@ private void setupResponse(final boolean result, final String resourceUuid) { final HostHAResponse response = new HostHAResponse(); response.setId(resourceUuid); response.setProvider(getHaProvider().toLowerCase()); + response.setStatus(result); response.setResponseName(getCommandName()); setResponseObject(response); } @@ -102,7 +103,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE if (!result) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to configure HA provider for the host"); } - CallContext.current().setEventDetails("Host Id:" + host.getId() + " HA configured with provider: " + getHaProvider()); + CallContext.current().setEventDetails("Host ID:" + host.getUuid() + " HA configured with provider: " + getHaProvider()); CallContext.current().putContextParameter(Host.class, host.getUuid()); setupResponse(result, host.getUuid()); @@ -115,6 +116,6 @@ public String getEventType() { @Override public String getEventDescription() { - return "configure HA for host: " + getHostId(); + return "Configuring HA for host with ID: " + getResourceUuid(ApiConstants.HOST_ID); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/DisableHAForClusterCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/DisableHAForClusterCmd.java index d570746765be..63c657a9e454 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/DisableHAForClusterCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/DisableHAForClusterCmd.java @@ -89,7 +89,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to find cluster by ID: " + getClusterId()); } final boolean result = haConfigManager.disableHA(cluster); - CallContext.current().setEventDetails("Cluster Id:" + cluster.getId() + " HA enabled: false"); + CallContext.current().setEventDetails("Cluster ID:" + cluster.getUuid() + " HA enabled: false"); CallContext.current().putContextParameter(Cluster.class, cluster.getUuid()); setupResponse(result); @@ -102,7 +102,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "disable HA for cluster: " + getClusterId(); + return "Disabling HA for cluster with ID: " + getResourceUuid(ApiConstants.CLUSTER_ID); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/DisableHAForHostCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/DisableHAForHostCmd.java index 5a8b1f1954f2..b90f731ff565 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/DisableHAForHostCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/DisableHAForHostCmd.java @@ -91,7 +91,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE } final boolean result = haConfigManager.disableHA(host.getId(), HAResource.ResourceType.Host); - CallContext.current().setEventDetails("Host Id:" + host.getId() + " HA enabled: false"); + CallContext.current().setEventDetails("Host ID:" + host.getUuid() + " HA enabled: false"); CallContext.current().putContextParameter(Host.class, host.getUuid()); setupResponse(result, host.getUuid()); @@ -104,6 +104,6 @@ public String getEventType() { @Override public String getEventDescription() { - return "disable HA for host: " + getHostId(); + return "Disabling HA for host with ID: " + getResourceUuid(ApiConstants.HOST_ID); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/DisableHAForZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/DisableHAForZoneCmd.java index fbd57fa82a15..07a6fbd2b399 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/DisableHAForZoneCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/DisableHAForZoneCmd.java @@ -90,7 +90,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE } final boolean result = haConfigManager.disableHA(dataCenter); - CallContext.current().setEventDetails("Zone Id:" + dataCenter.getId() + " HA enabled: false"); + CallContext.current().setEventDetails("Zone ID:" + dataCenter.getUuid() + " HA enabled: false"); CallContext.current().putContextParameter(DataCenter.class, dataCenter.getUuid()); setupResponse(result); @@ -103,7 +103,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "disable HA for zone: " + getZoneId(); + return "Disabling HA for zone with ID: " + getResourceUuid(ApiConstants.ZONE_ID); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/EnableHAForClusterCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/EnableHAForClusterCmd.java index 7e627939ca55..635fba988c60 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/EnableHAForClusterCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/EnableHAForClusterCmd.java @@ -90,7 +90,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE } final boolean result = haConfigManager.enableHA(cluster); - CallContext.current().setEventDetails("Cluster Id:" + cluster.getId() + " HA enabled: true"); + CallContext.current().setEventDetails("Cluster ID:" + cluster.getUuid() + " HA enabled: true"); CallContext.current().putContextParameter(Cluster.class, cluster.getUuid()); setupResponse(result); @@ -103,6 +103,6 @@ public String getEventType() { @Override public String getEventDescription() { - return "enable HA for cluster: " + getClusterId(); + return "Enabling HA for cluster with ID: " + getResourceUuid(ApiConstants.CLUSTER_ID); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/EnableHAForHostCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/EnableHAForHostCmd.java index aac3d3505198..0bda19a7ad3c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/EnableHAForHostCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/EnableHAForHostCmd.java @@ -91,7 +91,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE } final boolean result = haConfigManager.enableHA(host.getId(), HAResource.ResourceType.Host); - CallContext.current().setEventDetails("Host Id:" + host.getId() + " HA enabled: true"); + CallContext.current().setEventDetails("Host ID:" + host.getUuid() + " HA enabled: true"); CallContext.current().putContextParameter(Host.class, host.getUuid()); setupResponse(result, host.getUuid()); @@ -104,6 +104,6 @@ public String getEventType() { @Override public String getEventDescription() { - return "enable HA for host: " + getHostId(); + return "Enabling HA for host with ID: " + getResourceUuid(ApiConstants.HOST_ID); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/EnableHAForZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/EnableHAForZoneCmd.java index f9b1560bd8a1..f6d0f62bb120 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/EnableHAForZoneCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/ha/EnableHAForZoneCmd.java @@ -90,7 +90,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE } final boolean result = haConfigManager.enableHA(dataCenter); - CallContext.current().setEventDetails("Zone Id:" + dataCenter.getId() + " HA enabled: true"); + CallContext.current().setEventDetails("Zone ID:" + dataCenter.getUuid() + " HA enabled: true"); CallContext.current().putContextParameter(DataCenter.class, dataCenter.getUuid()); setupResponse(result); @@ -103,7 +103,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "enable HA for zone: " + getZoneId(); + return "Enabling HA for zone with ID: " + getResourceUuid(ApiConstants.ZONE_ID); } } 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 cc124ab8106e..5a1758345609 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 @@ -45,34 +45,35 @@ public class AddHostCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "the cluster ID for the host") + @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "The cluster ID for the host") private Long clusterId; - @Parameter(name = ApiConstants.CLUSTER_NAME, type = CommandType.STRING, description = "the cluster name for the host") + @Parameter(name = ApiConstants.CLUSTER_NAME, type = CommandType.STRING, description = "The cluster name for the host") private String clusterName; - @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, description = "the username for the host; required to be passed for hypervisors other than VMWare") + @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, description = "The username for the host; required to be passed for hypervisors other than VMWare") private String username; - @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, description = "the password for the host; required to be passed for hypervisors other than VMWare") + @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, description = "The password for the host; required to be passed for hypervisors other than VMWare") private String password; - @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, required = true, description = "the Pod ID for the host") + @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, required = true, description = "The Pod ID for the host") private Long podId; - @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, description = "the host URL") + @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, description = "The host URL, optionally add ssh port (format: 'host:port') for KVM hosts," + + " otherwise falls back to the port defined at the config 'kvm.host.discovery.ssh.port'") private String url; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "the Zone ID for the host") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "The Zone ID for the host") private Long zoneId; - @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, required = true, description = "hypervisor type of the host") + @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, required = true, description = "Hypervisor type of the host") private String hypervisor; @Parameter(name = ApiConstants.ALLOCATION_STATE, type = CommandType.STRING, description = "Allocation state of this Host for allocation of new resources") private String allocationState; - @Parameter(name = ApiConstants.HOST_TAGS, type = CommandType.LIST, collectionType = CommandType.STRING, description = "list of tags to be added to the host") + @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, diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/AddSecondaryStorageCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/AddSecondaryStorageCmd.java index c965a39450bd..585fd1b87a88 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/AddSecondaryStorageCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/AddSecondaryStorageCmd.java @@ -29,6 +29,11 @@ import com.cloud.exception.DiscoveryException; import com.cloud.storage.ImageStore; import com.cloud.user.Account; +import org.apache.commons.collections.MapUtils; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; @APICommand(name = "addSecondaryStorage", description = "Adds secondary storage.", responseObject = ImageStoreResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -38,12 +43,15 @@ public class AddSecondaryStorageCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, description = "the URL for the secondary storage") + @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, description = "The URL for the secondary storage") protected String url; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the Zone ID for the secondary storage") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The Zone ID for the secondary storage") protected Long zoneId; + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "Details in key/value pairs using format details[i].keyname=keyvalue. Example: details[0].copytemplatesfromothersecondarystorages=true") + protected Map details; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -56,6 +64,20 @@ public Long getZoneId() { return zoneId; } + public Map getDetails() { + Map detailsMap = new HashMap<>(); + if (MapUtils.isNotEmpty(details)) { + Collection props = details.values(); + for (Object prop : props) { + HashMap detail = (HashMap) prop; + for (Map.Entry entry: detail.entrySet()) { + detailsMap.put(entry.getKey(),entry.getValue()); + } + } + } + return detailsMap; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -68,7 +90,7 @@ public long getEntityOwnerId() { @Override public void execute(){ try{ - ImageStore result = _storageService.discoverImageStore(null, getUrl(), "NFS", getZoneId(), null); + ImageStore result = _storageService.discoverImageStore(null, getUrl(), "NFS", getZoneId(), getDetails()); ImageStoreResponse storeResponse = null; if (result != null ) { storeResponse = _responseGenerator.createImageStoreResponse(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/CancelHostAsDegradedCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/CancelHostAsDegradedCmd.java index 4e9d997b3b76..56930d47b2ec 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/CancelHostAsDegradedCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/CancelHostAsDegradedCmd.java @@ -47,7 +47,7 @@ public class CancelHostAsDegradedCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = BaseCmd.CommandType.UUID, entityType = HostResponse.class, description = "host ID", required = true, validations = {ApiArgValidator.PositiveNumber}) + @Parameter(name = ApiConstants.ID, type = BaseCmd.CommandType.UUID, entityType = HostResponse.class, description = "Host ID", required = true, validations = {ApiArgValidator.PositiveNumber}) private Long id; ///////////////////////////////////////////////////// @@ -78,7 +78,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "declaring host: " + getId() + " as Degraded"; + return "Removing host with ID: " + getResourceUuid(ApiConstants.ID) + " from Degraded state."; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/CancelHostMaintenanceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/CancelHostMaintenanceCmd.java index 55fe8ec23cec..5d44bafb4b5c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/CancelHostMaintenanceCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/CancelHostMaintenanceCmd.java @@ -40,7 +40,7 @@ public class CancelHostMaintenanceCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, description = "the host ID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, description = "The host ID") private Long id; ///////////////////////////////////////////////////// @@ -76,7 +76,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "canceling maintenance for host: " + getId(); + return "Canceling maintenance for host with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/DeclareHostAsDegradedCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/DeclareHostAsDegradedCmd.java index 6bb8f382e541..1dd65a583706 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/DeclareHostAsDegradedCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/DeclareHostAsDegradedCmd.java @@ -33,7 +33,7 @@ import org.apache.cloudstack.context.CallContext; @APICommand(name = "declareHostAsDegraded", - description = "Declare host as 'Degraded'. Host must be on 'Disconnected' or 'Alert' state. The ADMIN must be sure that there are no VMs running on the respective host otherwise this command might corrupted VMs that were running on the 'Degraded' host.", + description = "Declare host as 'Degraded'. Host must be on 'Disconnected' or 'Alert' state. The ADMIN must be sure that there are no Instances running on the respective host otherwise this command might corrupted Instances that were running on the 'Degraded' host.", since = "4.16.0.0", responseObject = HostResponse.class, requestHasSensitiveInfo = false, @@ -47,7 +47,7 @@ public class DeclareHostAsDegradedCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = BaseCmd.CommandType.UUID, entityType = HostResponse.class, description = "host ID", required = true, validations = {ApiArgValidator.PositiveNumber}) + @Parameter(name = ApiConstants.ID, type = BaseCmd.CommandType.UUID, entityType = HostResponse.class, description = "Host ID", required = true, validations = {ApiArgValidator.PositiveNumber}) private Long id; ///////////////////////////////////////////////////// @@ -78,7 +78,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "declaring host: " + getId() + " as Degraded"; + return "Declaring host with ID: " + getResourceUuid(ApiConstants.ID) + " as Degraded."; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/DeleteHostCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/DeleteHostCmd.java index 38325c2f072d..79ad1acec9f6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/DeleteHostCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/DeleteHostCmd.java @@ -37,17 +37,17 @@ public class DeleteHostCmd extends BaseCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, description = "the host ID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, description = "The host ID") private Long id; @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, - description = "Force delete the host. All HA enabled vms running on the host will be put to HA; HA disabled ones will be stopped") + description = "Force delete the host. All HA enabled Instances running on the host will be put to HA; HA disabled ones will be stopped") private Boolean forced; @Parameter(name = ApiConstants.FORCED_DESTROY_LOCAL_STORAGE, type = CommandType.BOOLEAN, - description = "Force destroy local storage on this host. All VMs created on this local storage will be destroyed") + description = "Force destroy local storage on this host. All Instances created on this local storage will be destroyed") private Boolean forceDestroyLocalStorage; // /////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/FindHostsForMigrationCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/FindHostsForMigrationCmd.java index db30e4f4c02f..4d6ef7419616 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/FindHostsForMigrationCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/FindHostsForMigrationCmd.java @@ -33,7 +33,7 @@ import com.cloud.utils.Pair; import com.cloud.utils.Ternary; -@APICommand(name = "findHostsForMigration", description = "Find hosts suitable for migrating a virtual machine.", responseObject = HostForMigrationResponse.class, +@APICommand(name = "findHostsForMigration", description = "Find hosts suitable for migrating an Instance.", responseObject = HostForMigrationResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class FindHostsForMigrationCmd extends BaseListCmd { @@ -46,7 +46,7 @@ public class FindHostsForMigrationCmd extends BaseListCmd { type = CommandType.UUID, entityType = UserVmResponse.class, required = true, - description = "find hosts to which this VM can be migrated and flag the hosts with enough " + "CPU/RAM to host the VM") + description = "Find hosts to which this Instance can be migrated and flag the hosts with enough " + "CPU/RAM to host the Instance") private Long virtualMachineId; ///////////////////////////////////////////////////// @@ -78,7 +78,7 @@ public void execute() { for (Host host : result.first()) { HostForMigrationResponse hostResponse = _responseGenerator.createHostForMigrationResponse(host); Boolean suitableForMigration = false; - if (hostsWithCapacity.contains(host)) { + if (hostsWithCapacity != null && hostsWithCapacity.contains(host)) { suitableForMigration = true; } hostResponse.setSuitableForMigration(suitableForMigration); 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 a71150a69e27..8f5e6c784d6e 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 @@ -52,59 +52,59 @@ public class ListHostsCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "lists hosts existing in particular cluster") + @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "Lists hosts existing in particular cluster") private Long clusterId; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HostResponse.class, description = "the id of the host") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HostResponse.class, description = "The ID of the host") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the host") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the host") private String hostName; - @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "the Pod ID for the host") + @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "The Pod ID for the host") private Long podId; - @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "the state of the host") + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "The state of the host") private String state; - @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "the host type") + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "The host type") private String type; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the Zone ID for the host") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The Zone ID for the host") private Long zoneId; @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = false, - description = "lists hosts in the same cluster as this VM and flag hosts with enough CPU/RAm to host this VM") + description = "Lists hosts in the same cluster as this Instance and flag hosts with enough CPU/RAm to host this Instance") private Long virtualMachineId; @Parameter(name = ApiConstants.OUTOFBANDMANAGEMENT_ENABLED, type = CommandType.BOOLEAN, - description = "list hosts for which out-of-band management is enabled") + description = "List hosts for which out-of-band management is enabled") private Boolean outOfBandManagementEnabled; @Parameter(name = ApiConstants.OUTOFBANDMANAGEMENT_POWERSTATE, type = CommandType.STRING, - description = "list hosts by its out-of-band management interface's power state. Its value can be one of [On, Off, Unknown]") + description = "List hosts by its out-of-band management interface's power state. Its value can be one of [On, Off, Unknown]") private String outOfBandManagementPowerState; @Parameter(name = ApiConstants.RESOURCE_STATE, type = CommandType.STRING, - description = "list hosts by resource state. Resource state represents current state determined by admin of host, value can be one of [Enabled, Disabled, Unmanaged, PrepareForMaintenance, ErrorInMaintenance, Maintenance, Error]") + description = "List hosts by resource state. Resource state represents current state determined by admin of host, value can be one of [Enabled, Disabled, Unmanaged, PrepareForMaintenance, ErrorInMaintenance, Maintenance, Error]") private String resourceState; @Parameter(name = ApiConstants.DETAILS, type = CommandType.LIST, collectionType = CommandType.STRING, - description = "comma separated list of host details requested, value can be a list of [ min, all, capacity, events, stats]") + description = "Comma separated list of host details requested, value can be a list of [ min, all, capacity, events, stats]") private List viewDetails; - @Parameter(name = ApiConstants.HA_HOST, type = CommandType.BOOLEAN, description = "if true, list only hosts dedicated to HA") + @Parameter(name = ApiConstants.HA_HOST, type = CommandType.BOOLEAN, description = "If true, list only hosts dedicated to HA") private Boolean haHost; - @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "hypervisor type of host: XenServer,KVM,VMware,Hyperv,BareMetal,Simulator") + @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "Hypervisor type of host: XenServer,KVM,VMware,Hyperv,BareMetal,Simulator") private String hypervisor; @Parameter(name = ApiConstants.MANAGEMENT_SERVER_ID, type = CommandType.UUID, entityType = ManagementServerResponse.class, description = "the id of the management server", since="4.21.0") @@ -118,6 +118,9 @@ public class ListHostsCmd extends BaseListCmd { since = "4.21.0") private String storageAccessGroup; + @Parameter(name = ApiConstants.VERSION, type = CommandType.STRING, description = "the host version", since = "4.20.3") + private String version; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -222,6 +225,10 @@ public ListHostsCmd(String storageAccessGroup) { this.storageAccessGroup = storageAccessGroup; } + public String getVersion() { + return version; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -245,7 +252,7 @@ protected ListResponse getHostResponses() { for (Host host : result.first()) { HostResponse hostResponse = _responseGenerator.createHostResponse(host, getDetails()); Boolean suitableForMigration = false; - if (hostsWithCapacity.contains(host)) { + if (hostsWithCapacity != null && hostsWithCapacity.contains(host)) { suitableForMigration = true; } hostResponse.setSuitableForMigration(suitableForMigration); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/PrepareForHostMaintenanceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/PrepareForHostMaintenanceCmd.java index 5c2b50c87239..843c7fd7fcbe 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/PrepareForHostMaintenanceCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/PrepareForHostMaintenanceCmd.java @@ -40,7 +40,7 @@ public class PrepareForHostMaintenanceCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, description = "the host ID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, description = "The host ID") private Long id; ///////////////////////////////////////////////////// @@ -76,7 +76,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "preparing host: " + getId() + " for maintenance"; + return "Preparing host with ID: " + getResourceUuid(ApiConstants.ID) + " for maintenance."; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/ReconnectHostCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/ReconnectHostCmd.java index 3550d61fdb97..b9892ed6033c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/ReconnectHostCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/ReconnectHostCmd.java @@ -41,7 +41,7 @@ public class ReconnectHostCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, description = "the host ID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, description = "The host ID") private Long id; ///////////////////////////////////////////////////// @@ -77,7 +77,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "reconnecting host: " + getId(); + return "Reconnecting host with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/ReleaseHostReservationCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/ReleaseHostReservationCmd.java index 7fee0684c781..bddb5b13e452 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/ReleaseHostReservationCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/ReleaseHostReservationCmd.java @@ -40,7 +40,7 @@ public class ReleaseHostReservationCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, description = "the host ID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, description = "The host ID") private Long id; ///////////////////////////////////////////////////// @@ -72,7 +72,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "releasing reservation for host: " + getId(); + return "Releasing reservation from host with ID: " + getResourceUuid(ApiConstants.ID); } @Override 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 9baea58b7b52..69bca7c79e21 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 @@ -40,7 +40,7 @@ public class UpdateHostCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, description = "the ID of the host to update") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, description = "The ID of the host to update") private Long id; @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Change the name of host", since = "4.15", authorized = {RoleType.Admin}) @@ -49,21 +49,26 @@ public class UpdateHostCmd extends BaseCmd { @Parameter(name = ApiConstants.OS_CATEGORY_ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, - description = "the id of Os category to update the host with") + description = "the ID of OS category used to prioritize VMs with matching OS category during the allocation process. " + + "It cannot be used alongside the 'guestosrule' parameter.") private Long osCategoryId; + @Parameter(name = ApiConstants.GUEST_OS_RULE, type = CommandType.STRING, description = "the guest OS rule written in JavaScript to match with the OS of the VM." + + "It cannot be used alongside the 'oscategoryid' parameter.") + private String guestOsRule; + @Parameter(name = ApiConstants.ALLOCATION_STATE, type = CommandType.STRING, description = "Change resource state of host, valid values are [Enable, Disable]. Operation may failed if host in states not allowing Enable/Disable") private String allocationState; - @Parameter(name = ApiConstants.HOST_TAGS, type = CommandType.LIST, collectionType = CommandType.STRING, description = "list of tags to be added to the host") + @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.IS_TAG_A_RULE, type = CommandType.BOOLEAN, description = ApiConstants.PARAMETER_DESCRIPTION_IS_TAG_A_RULE) private Boolean isTagARule; - @Parameter(name = ApiConstants.URL, type = CommandType.STRING, description = "the new uri for the secondary storage: nfs://host/path") + @Parameter(name = ApiConstants.URL, type = CommandType.STRING, description = "The new URI for the secondary storage: nfs://host/path") private String url; @Parameter(name = ApiConstants.ANNOTATION, type = CommandType.STRING, description = "Add an annotation to this host", since = "4.11", authorized = {RoleType.Admin}) @@ -72,6 +77,14 @@ public class UpdateHostCmd extends BaseCmd { @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; + @Parameter(name = ApiConstants.CLEAN_UP_EXTERNAL_DETAILS, + type = CommandType.BOOLEAN, + description = "Optional boolean field, which indicates if external details should be cleaned up or not " + + "(If set to true, external details removed for this host, externaldetails field ignored; " + + "if false or not set, no action)", + since = "4.22.0") + protected Boolean cleanupExternalDetails; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -88,6 +101,10 @@ public Long getOsCategoryId() { return osCategoryId; } + public String getGuestOsRule() { + return guestOsRule; + } + public String getAllocationState() { return allocationState; } @@ -112,6 +129,10 @@ public Map getExternalDetails() { return convertExternalDetailsToMap(externalDetails); } + public boolean isCleanupExternalDetails() { + return Boolean.TRUE.equals(cleanupExternalDetails); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -135,7 +156,7 @@ public void execute() { this.setResponseObject(hostResponse); } catch (Exception e) { Host host = _entityMgr.findById(Host.class, getId()); - logger.debug("Failed to update host: {} with id {}", host, getId(), e); + logger.error("Failed to update {}", host, e); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to update host: %s with id %d, %s", host, getId(), e.getMessage())); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/UpdateHostPasswordCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/UpdateHostPasswordCmd.java index c94fe2c58656..f96f01d3a877 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/UpdateHostPasswordCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/UpdateHostPasswordCmd.java @@ -36,19 +36,19 @@ public class UpdateHostPasswordCmd extends BaseCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "the host ID") + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "The host ID") private Long hostId; - @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "the cluster ID") + @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "The cluster ID") private Long clusterId; - @Parameter(name = ApiConstants.SHOULD_UPDATE_PASSWORD, type = CommandType.BOOLEAN, description = "if the password should also be updated on the hosts") + @Parameter(name = ApiConstants.SHOULD_UPDATE_PASSWORD, type = CommandType.BOOLEAN, description = "If the password should also be updated on the hosts") private Boolean updatePasswdOnHost; - @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, required = true, description = "the username for the host/cluster") + @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, required = true, description = "The username for the host/cluster") private String username; - @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, required = true, description = "the new password for the host/cluster") + @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, required = true, description = "The new password for the host/cluster") private String password; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/ConfigureInternalLoadBalancerElementCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/ConfigureInternalLoadBalancerElementCmd.java index c94d326ee622..51aa86546603 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/ConfigureInternalLoadBalancerElementCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/ConfigureInternalLoadBalancerElementCmd.java @@ -50,7 +50,7 @@ public class ConfigureInternalLoadBalancerElementCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = InternalLoadBalancerElementResponse.class, required = true, - description = "the ID of the internal lb provider") + description = "The ID of the internal lb provider") private Long id; @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, required = true, description = "Enables/Disables the Internal Load Balancer element") @@ -84,12 +84,12 @@ public String getEventType() { @Override public String getEventDescription() { - return "configuring internal load balancer element: " + id; + return "Configuring internal load balancer element with ID: " + getResourceUuid(ApiConstants.ID); } @Override public void execute() throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { - CallContext.current().setEventDetails("Internal load balancer element: " + id); + CallContext.current().setEventDetails("Internal load balancer element: " + getResourceUuid(ApiConstants.ID)); InternalLoadBalancerElementService service = _networkService.getInternalLoadBalancerElementById(id); VirtualRouterProvider result = service.configureInternalLoadBalancerElement(getId(), getEnabled()); if (result != null) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/CreateInternalLoadBalancerElementCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/CreateInternalLoadBalancerElementCmd.java index 924287b673ba..aa9e5f1ba7f4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/CreateInternalLoadBalancerElementCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/CreateInternalLoadBalancerElementCmd.java @@ -48,7 +48,7 @@ public class CreateInternalLoadBalancerElementCmd extends BaseAsyncCreateCmd { type = CommandType.UUID, entityType = ProviderResponse.class, required = true, - description = "the network service provider ID of the internal load balancer element") + description = "The network service provider ID of the internal load balancer element") private Long nspId; ///////////////////////////////////////////////////// @@ -74,7 +74,7 @@ public long getEntityOwnerId() { @Override public void execute() { - CallContext.current().setEventDetails("Virtual router element Id: " + getEntityId()); + CallContext.current().setEventDetails("Virtual router element ID: " + getEntityUuid()); InternalLoadBalancerElementService service = _networkService.getInternalLoadBalancerElementByNetworkServiceProviderId(getNspId()); VirtualRouterProvider result = service.getInternalLoadBalancerElement(getEntityId()); if (result != null) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/ListInternalLBVMsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/ListInternalLBVMsCmd.java index 0eb00234382d..9ba23279c5df 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/ListInternalLBVMsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/ListInternalLBVMsCmd.java @@ -35,7 +35,7 @@ import com.cloud.network.router.VirtualRouter.Role; import com.cloud.vm.VirtualMachine; -@APICommand(name = "listInternalLoadBalancerVMs", description = "List internal LB VMs.", responseObject = DomainRouterResponse.class, entityType = {VirtualMachine.class}, +@APICommand(name = "listInternalLoadBalancerVMs", description = "List internal LB Instances.", responseObject = DomainRouterResponse.class, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListInternalLBVMsCmd extends BaseListProjectAndAccountResourcesCmd { @@ -45,36 +45,36 @@ public class ListInternalLBVMsCmd extends BaseListProjectAndAccountResourcesCmd //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "the host ID of the Internal LB VM") + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "The host ID of the Internal LB Instance") private Long hostId; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, description = "the ID of the Internal LB VM") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, description = "The ID of the Internal LB Instance") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the Internal LB VM") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the Internal LB Instance") private String routerName; - @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "the Pod ID of the Internal LB VM") + @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "The Pod ID of the Internal LB Instance") private Long podId; - @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "the state of the Internal LB VM") + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "The state of the Internal LB Instance") private String state; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the Zone ID of the Internal LB VM") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The Zone ID of the Internal LB Instance") private Long zoneId; - @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "list by network id") + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "List by Network ID") private Long networkId; - @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, description = "List Internal LB VMs by VPC") + @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, description = "List Internal LB Instances by VPC") private Long vpcId; - @Parameter(name = ApiConstants.FOR_VPC, type = CommandType.BOOLEAN, description = "if true is passed for this parameter, list only VPC Internal LB VMs") + @Parameter(name = ApiConstants.FOR_VPC, type = CommandType.BOOLEAN, description = "If true is passed for this parameter, list only VPC Internal LB Instances") private Boolean forVpc; @Parameter(name = ApiConstants.FETCH_ROUTER_HEALTH_CHECK_RESULTS, type = CommandType.BOOLEAN, since = "4.14", - description = "if true is passed for this parameter, also fetch last executed health check results for the VM. Default is false") + description = "If true is passed for this parameter, also fetch last executed health check results for the Instance. Default is false") private Boolean fetchHealthCheckResults; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/ListInternalLoadBalancerElementsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/ListInternalLoadBalancerElementsCmd.java index b17cc22e7466..9cd7ba2656b5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/ListInternalLoadBalancerElementsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/ListInternalLoadBalancerElementsCmd.java @@ -50,16 +50,16 @@ public class ListInternalLoadBalancerElementsCmd extends BaseListCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = InternalLoadBalancerElementResponse.class, - description = "list internal load balancer elements by id") + description = "List internal load balancer elements by ID") private Long id; @Parameter(name = ApiConstants.NSP_ID, type = CommandType.UUID, entityType = ProviderResponse.class, - description = "list internal load balancer elements by network service provider id") + description = "List internal load balancer elements by network service provider ID") private Long nspId; - @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, description = "list internal load balancer elements by enabled state") + @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, description = "List internal load balancer elements by enabled state") private Boolean enabled; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/StartInternalLBVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/StartInternalLBVMCmd.java index 3dd7d2adf378..d9d4e46726fc 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/StartInternalLBVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/StartInternalLBVMCmd.java @@ -38,7 +38,7 @@ import com.cloud.network.router.VirtualRouter.Role; import com.cloud.vm.VirtualMachine; -@APICommand(name = "startInternalLoadBalancerVM", responseObject = DomainRouterResponse.class, description = "Starts an existing internal lb vm.", entityType = {VirtualMachine.class}, +@APICommand(name = "startInternalLoadBalancerVM", responseObject = DomainRouterResponse.class, description = "Starts an existing Internal LB Instance.", entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class StartInternalLBVMCmd extends BaseAsyncCmd { private static final String s_name = "startinternallbvmresponse"; @@ -47,7 +47,7 @@ public class StartInternalLBVMCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DomainRouterResponse.class, required = true, description = "the ID of the internal lb vm") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DomainRouterResponse.class, required = true, description = "The ID of the Internal LB Instance") private Long id; ///////////////////////////////////////////////////// @@ -77,7 +77,7 @@ public long getEntityOwnerId() { if (router != null && router.getRole() == Role.INTERNAL_LB_VM) { return router.getAccountId(); } else { - throw new InvalidParameterValueException("Unable to find internal lb vm by id"); + throw new InvalidParameterValueException("Unable to find Internal LB Instance by ID"); } } @@ -88,7 +88,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "starting internal lb vm: " + getId(); + return "Starting internal LB Instance with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -103,11 +103,11 @@ public Long getApiResourceId() { @Override public void execute() throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { - CallContext.current().setEventDetails("Internal Lb Vm Id: " + getId()); + CallContext.current().setEventDetails("Internal LB Instance ID: " + getResourceUuid(ApiConstants.ID)); VirtualRouter result = null; VirtualRouter router = _routerService.findRouter(getId()); if (router == null || router.getRole() != Role.INTERNAL_LB_VM) { - throw new InvalidParameterValueException("Can't find internal lb vm by id"); + throw new InvalidParameterValueException("Can't find Internal LB Instance by ID"); } else { result = _internalLbSvc.startInternalLbVm(getId(), CallContext.current().getCallingAccount(), CallContext.current().getCallingUserId()); } @@ -117,7 +117,7 @@ public void execute() throws ConcurrentOperationException, ResourceUnavailableEx routerResponse.setResponseName(getCommandName()); setResponseObject(routerResponse); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to start internal lb vm"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to start Internal LB Instance"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/StopInternalLBVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/StopInternalLBVMCmd.java index a746e5d906d6..253c59e671e5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/StopInternalLBVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/StopInternalLBVMCmd.java @@ -37,7 +37,7 @@ import com.cloud.network.router.VirtualRouter.Role; import com.cloud.vm.VirtualMachine; -@APICommand(name = "stopInternalLoadBalancerVM", description = "Stops an Internal LB vm.", responseObject = DomainRouterResponse.class, entityType = {VirtualMachine.class}, +@APICommand(name = "stopInternalLoadBalancerVM", description = "Stops an Internal LB Instance.", responseObject = DomainRouterResponse.class, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class StopInternalLBVMCmd extends BaseAsyncCmd { private static final String s_name = "stopinternallbvmresponse"; @@ -46,10 +46,10 @@ public class StopInternalLBVMCmd extends BaseAsyncCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DomainRouterResponse.class, required = true, description = "the ID of the internal lb vm") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DomainRouterResponse.class, required = true, description = "The ID of the internal LB Instance") private Long id; - @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force stop the VM (vm is marked as Stopped even when command fails to be send to the backend, otherwise a force poweroff is attempted). To be used if the caller knows the VM is stopped and should be marked as such.") + @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force stop the Instance (Instance is marked as Stopped even when command fails to be send to the backend, otherwise a force poweroff is attempted). To be used if the caller knows the Instance is stopped and should be marked as such.") private Boolean forced; // /////////////////////////////////////////////////// @@ -75,7 +75,7 @@ public long getEntityOwnerId() { if (vm != null && vm.getRole() == Role.INTERNAL_LB_VM) { return vm.getAccountId(); } else { - throw new InvalidParameterValueException("Unable to find internal lb vm by id"); + throw new InvalidParameterValueException("Unable to find Internal LB Instance by ID"); } } @@ -86,7 +86,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "stopping internal lb vm: " + getId(); + return "Stopping Internal LB Instance: " + getResourceUuid(ApiConstants.ID); } @Override @@ -105,11 +105,11 @@ public boolean isForced() { @Override public void execute() throws ConcurrentOperationException, ResourceUnavailableException { - CallContext.current().setEventDetails("Internal lb vm Id: " + getId()); + CallContext.current().setEventDetails("Internal LB Instance ID: " + getResourceUuid(ApiConstants.ID)); VirtualRouter result = null; VirtualRouter vm = _routerService.findRouter(getId()); if (vm == null || vm.getRole() != Role.INTERNAL_LB_VM) { - throw new InvalidParameterValueException("Can't find internal lb vm by id"); + throw new InvalidParameterValueException("Can't find Internal LB Instance by ID"); } else { result = _internalLbSvc.stopInternalLbVm(getId(), isForced(), CallContext.current().getCallingAccount(), CallContext.current().getCallingUserId()); } @@ -119,7 +119,7 @@ public void execute() throws ConcurrentOperationException, ResourceUnavailableEx response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to stop internal lb vm"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to stop Internal LB Instance"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/iso/AttachIsoCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/iso/AttachIsoCmdByAdmin.java index e39107b2593c..d253580f0983 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/iso/AttachIsoCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/iso/AttachIsoCmdByAdmin.java @@ -22,6 +22,6 @@ import org.apache.cloudstack.api.command.user.iso.AttachIsoCmd; import org.apache.cloudstack.api.response.UserVmResponse; -@APICommand(name = "attachIso", description = "Attaches an ISO to a virtual machine.", responseObject = UserVmResponse.class, responseView = ResponseView.Full, +@APICommand(name = "attachIso", description = "Attaches an ISO to an Instance.", responseObject = UserVmResponse.class, responseView = ResponseView.Full, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class AttachIsoCmdByAdmin extends AttachIsoCmd implements AdminCmd { } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/iso/CopyIsoCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/iso/CopyIsoCmdByAdmin.java index f27c0c5c234a..f64326638a33 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/iso/CopyIsoCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/iso/CopyIsoCmdByAdmin.java @@ -21,7 +21,7 @@ import org.apache.cloudstack.api.command.user.iso.CopyIsoCmd; import org.apache.cloudstack.api.response.TemplateResponse; -@APICommand(name = "copyIso", description = "Copies an iso from one zone to another.", responseObject = TemplateResponse.class, responseView = ResponseView.Full, +@APICommand(name = "copyIso", description = "Copies an ISO from one zone to another.", responseObject = TemplateResponse.class, responseView = ResponseView.Full, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CopyIsoCmdByAdmin extends CopyIsoCmd { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/iso/DetachIsoCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/iso/DetachIsoCmdByAdmin.java index 5eeba2bfa301..553d0ae13c69 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/iso/DetachIsoCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/iso/DetachIsoCmdByAdmin.java @@ -22,6 +22,6 @@ import org.apache.cloudstack.api.command.user.iso.DetachIsoCmd; import org.apache.cloudstack.api.response.UserVmResponse; -@APICommand(name = "detachIso", description = "Detaches any ISO file (if any) currently attached to a virtual machine.", responseObject = UserVmResponse.class, responseView = ResponseView.Full, +@APICommand(name = "detachIso", description = "Detaches any ISO file (if any) currently attached to an Instance.", responseObject = UserVmResponse.class, responseView = ResponseView.Full, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class DetachIsoCmdByAdmin extends DetachIsoCmd implements AdminCmd {} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/iso/ListIsoPermissionsCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/iso/ListIsoPermissionsCmdByAdmin.java index 46bd4f3766e7..b831a99cb0af 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/iso/ListIsoPermissionsCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/iso/ListIsoPermissionsCmdByAdmin.java @@ -1,4 +1,4 @@ -// Licensedname = "listIsoPermissions", to the Apache Software Foundation (ASF) under one +// 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 @@ -23,7 +23,7 @@ import org.apache.cloudstack.api.response.TemplatePermissionsResponse; @APICommand(name = "listIsoPermissions", - description = "List iso visibility and all accounts that have permissions to view this iso.", + description = "List ISO visibility and all accounts that have permissions to view this ISO.", responseObject = TemplatePermissionsResponse.class, responseView = ResponseView.Full, requestHasSensitiveInfo = false, diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/kms/MigrateVolumesToKMSCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/kms/MigrateVolumesToKMSCmd.java new file mode 100644 index 000000000000..358f7f749f6e --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/kms/MigrateVolumesToKMSCmd.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.kms; + +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.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.AsyncJobResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.KMSKeyResponse; +import org.apache.cloudstack.api.response.VolumeResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.framework.kms.KMSException; +import org.apache.cloudstack.kms.KMSKey; +import org.apache.cloudstack.kms.KMSManager; + +import javax.inject.Inject; +import java.util.List; + +@APICommand(name = "migrateVolumesToKMS", + description = "Migrates encrypted volumes to KMS", + responseObject = AsyncJobResponse.class, + since = "4.23.0", + authorized = {RoleType.Admin}, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false) +public class MigrateVolumesToKMSCmd extends BaseAsyncCmd { + + @Inject + private KMSManager kmsManager; + + @Parameter(name = ApiConstants.ACCOUNT, + type = CommandType.STRING, + description = "Migrate volumes for specific account") + private String accountName; + + @Parameter(name = ApiConstants.DOMAIN_ID, + type = CommandType.UUID, + entityType = DomainResponse.class, + description = "Domain ID") + private Long domainId; + + @Parameter(name = ApiConstants.VOLUME_IDS, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = VolumeResponse.class, + required = true, + description = "List of volume IDs to migrate") + private List volumeIds; + + @Parameter(name = ApiConstants.KMS_KEY_ID, + required = true, + type = CommandType.UUID, + entityType = KMSKeyResponse.class, + description = "KMS Key ID to use for migrating volumes") + private Long kmsKeyId; + + public String getAccountName() { + return accountName; + } + + public Long getDomainId() { + return domainId; + } + + public List getVolumeIds() { + return volumeIds; + } + + public Long getKmsKeyId() { + return kmsKeyId; + } + + @Override + public void execute() { + try { + kmsManager.migrateVolumesToKMS(this); + } catch (KMSException e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + "Failed to migrate volumes to KMS: " + e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + KMSKey key = _entityMgr.findById(KMSKey.class, kmsKeyId); + if (key != null) { + return key.getAccountId(); + } + return CallContext.current().getCallingAccount().getId(); + } + + @Override + public String getEventType() { + return com.cloud.event.EventTypes.EVENT_VOLUME_MIGRATE_TO_KMS; + } + + @Override + public String getEventDescription() { + return "Migrating volumes to KMS key: " + _uuidMgr.getUuid(KMSKey.class, kmsKeyId); + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.KmsKey; + } + + @Override + public Long getApiResourceId() { + return kmsKeyId; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/loadbalancer/ListLoadBalancerRuleInstancesCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/loadbalancer/ListLoadBalancerRuleInstancesCmdByAdmin.java index b11988b241fb..a37e058924f6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/loadbalancer/ListLoadBalancerRuleInstancesCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/loadbalancer/ListLoadBalancerRuleInstancesCmdByAdmin.java @@ -23,7 +23,7 @@ import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.command.user.loadbalancer.ListLoadBalancerRuleInstancesCmd; -@APICommand(name = "listLoadBalancerRuleInstances", description = "List all virtual machine instances that are assigned to a load balancer rule.", responseObject = LoadBalancerRuleVmMapResponse.class, responseView = ResponseView.Full, +@APICommand(name = "listLoadBalancerRuleInstances", description = "List all Instances that are assigned to a load balancer rule.", responseObject = LoadBalancerRuleVmMapResponse.class, responseView = ResponseView.Full, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class ListLoadBalancerRuleInstancesCmdByAdmin extends ListLoadBalancerRuleInstancesCmd implements AdminCmd {} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/management/ListMgmtsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/management/ListMgmtsCmd.java index 7b7eae1d0e93..293cb34e7028 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/management/ListMgmtsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/management/ListMgmtsCmd.java @@ -34,10 +34,10 @@ public class ListMgmtsCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ManagementServerResponse.class, description = "the id of the management server") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ManagementServerResponse.class, description = "The ID of the management server") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the management server") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the management server") private String hostName; @Parameter(name = ApiConstants.PEERS, type = CommandType.BOOLEAN, @@ -45,6 +45,10 @@ public class ListMgmtsCmd extends BaseListCmd { since = "4.20.1.0") private Boolean peers; + @Parameter(name = ApiConstants.VERSION, type = CommandType.STRING, + description = "the version of the management server", since = "4.20.3") + private String version; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -61,6 +65,10 @@ public Boolean getPeers() { return BooleanUtils.toBooleanDefaultIfNull(peers, false); } + public String getVersion() { + return version; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/AddNetworkDeviceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/AddNetworkDeviceCmd.java index 334772970431..e90a56a92abb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/AddNetworkDeviceCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/AddNetworkDeviceCmd.java @@ -55,7 +55,7 @@ public class AddNetworkDeviceCmd extends BaseCmd { description = "Network device type, now supports ExternalDhcp, PxeServer, NetscalerMPXLoadBalancer, NetscalerVPXLoadBalancer, NetscalerSDXLoadBalancer, F5BigIpLoadBalancer, JuniperSRXFirewall, PaloAltoFirewall") private String type; - @Parameter(name = ApiConstants.NETWORK_DEVICE_PARAMETER_LIST, type = CommandType.MAP, description = "parameters for network device") + @Parameter(name = ApiConstants.NETWORK_DEVICE_PARAMETER_LIST, type = CommandType.MAP, description = "Parameters for network device") private Map paramList; public String getDeviceType() { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/AddNetworkServiceProviderCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/AddNetworkServiceProviderCmd.java index 40a822393452..3e42a0103d8b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/AddNetworkServiceProviderCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/AddNetworkServiceProviderCmd.java @@ -52,22 +52,22 @@ public class AddNetworkServiceProviderCmd extends BaseAsyncCreateCmd { type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, required = true, - description = "the Physical Network ID to add the provider to") + description = "The Physical Network ID to add the provider to") private Long physicalNetworkId; @Parameter(name = ApiConstants.DEST_PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, - description = "the destination Physical Network ID to bridge to") + description = "The destination Physical Network ID to bridge to") private Long destinationPhysicalNetworkId; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name for the physical network service provider") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "The name for the physical network service provider") private String name; @Parameter(name = ApiConstants.SERVICE_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, - description = "the list of services to be enabled for this physical network service provider") + description = "The list of services to be enabled for this physical network service provider") private List enabledServices; ///////////////////////////////////////////////////// @@ -101,7 +101,7 @@ public long getEntityOwnerId() { @Override public void execute() { - CallContext.current().setEventDetails("Network ServiceProvider Id: " + getEntityId()); + CallContext.current().setEventDetails("Network ServiceProvider ID: " + getEntityUuid()); PhysicalNetworkServiceProvider result = _networkService.getCreatedPhysicalNetworkServiceProvider(getEntityId()); if (result != null) { ProviderResponse response = _responseGenerator.createNetworkServiceProviderResponse(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CloneNetworkOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CloneNetworkOfferingCmd.java new file mode 100644 index 000000000000..19760ffaaa10 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CloneNetworkOfferingCmd.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.network; + +import java.util.List; + +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.NetworkOfferingResponse; + +import com.cloud.offering.NetworkOffering; + +@APICommand(name = "cloneNetworkOffering", + description = "Clones a network offering. All parameters are copied from the source offering unless explicitly overridden. " + + "Use 'addServices' and 'dropServices' to modify the service list without respecifying everything.", + responseObject = NetworkOfferingResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + since = "4.23.0") +public class CloneNetworkOfferingCmd extends NetworkOfferingBaseCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.SOURCE_OFFERING_ID, + type = BaseCmd.CommandType.UUID, + entityType = NetworkOfferingResponse.class, + required = true, + description = "The ID of the source network offering to clone from") + private Long sourceOfferingId; + + @Parameter(name = "addservices", + type = CommandType.LIST, + collectionType = CommandType.STRING, + description = "Services to add to the cloned offering (in addition to source offering services). " + + "If specified along with 'supportedservices', this parameter is ignored.") + private List addServices; + + @Parameter(name = "dropservices", + type = CommandType.LIST, + collectionType = CommandType.STRING, + description = "Services to remove from the cloned offering (that exist in source offering). " + + "If specified along with 'supportedservices', this parameter is ignored.") + private List dropServices; + + @Parameter(name = ApiConstants.TRAFFIC_TYPE, + type = CommandType.STRING, + description = "The traffic type for the network offering. Supported type in current release is GUEST only") + private String traffictype; + + @Parameter(name = ApiConstants.GUEST_IP_TYPE, type = CommandType.STRING, description = "Guest type of the network offering: Shared or Isolated") + private String guestIptype; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getSourceOfferingId() { + return sourceOfferingId; + } + + public List getAddServices() { + return addServices; + } + + public List getDropServices() { + return dropServices; + } + + public String getGuestIpType() { + return guestIptype; + } + + public String getTraffictype() { + return traffictype; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + NetworkOffering result = _configService.cloneNetworkOffering(this); + if (result != null) { + NetworkOfferingResponse response = _responseGenerator.createNetworkOfferingResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to clone network offering"); + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateGuestNetworkIpv6PrefixCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateGuestNetworkIpv6PrefixCmd.java index f6b035c57837..614dcf9d0751 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateGuestNetworkIpv6PrefixCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateGuestNetworkIpv6PrefixCmd.java @@ -83,7 +83,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Creating guest IPv6 prefix " + getPrefix() + " for zone=" + getZoneId(); + return "Creating guest IPv6 prefix " + getPrefix() + " for zone with ID: " + getResourceUuid(ApiConstants.ZONE_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmd.java index a482cb1d4f27..4d645376a909 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmd.java @@ -85,7 +85,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Creating guest IPv4 subnet " + getSubnet() + " in zone subnet=" + getParentId(); + return "Creating guest IPv4 subnet " + getSubnet() + " in zone subnet: " + getResourceUuid(ApiConstants.PARENT_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForZoneCmd.java index 5f48cf9c6327..48a6002fb5c0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForZoneCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForZoneCmd.java @@ -102,7 +102,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Creating guest IPv4 subnet " + getSubnet() + " for zone=" + getZoneId(); + return "Creating guest IPv4 subnet " + getSubnet() + " for zone: " + getResourceUuid(ApiConstants.ZONE_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateManagementNetworkIpRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateManagementNetworkIpRangeCmd.java index 85cfddfb714f..2780c4eaf050 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateManagementNetworkIpRangeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateManagementNetworkIpRangeCmd.java @@ -34,6 +34,7 @@ import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.user.Account; +import com.cloud.utils.StringUtils; @APICommand(name = "createManagementNetworkIpRange", description = "Creates a Management network IP range.", @@ -118,7 +119,7 @@ public Boolean isForSystemVms() { } public String getVlan() { - if (vlan == null || vlan.isEmpty()) { + if (StringUtils.isBlank(vlan)) { vlan = "untagged"; } return vlan; @@ -131,7 +132,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Creating management ip range from " + getStartIp() + " to " + getEndIp() + " and gateway=" + getGateWay() + ", netmask=" + getNetmask() + " of pod=" + getPodId(); + return "Creating management IP range from " + getStartIp() + " to " + getEndIp() + ", with gateway: " + getGateWay() + ", netmask:" + getNetmask() + " on pod:" + getPodId(); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkCmdByAdmin.java index d8b57f79528c..57d1d4fad116 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkCmdByAdmin.java @@ -31,24 +31,24 @@ import java.util.List; -@APICommand(name = "createNetwork", description = "Creates a network", responseObject = NetworkResponse.class, responseView = ResponseView.Full, entityType = {Network.class}, +@APICommand(name = "createNetwork", description = "Creates a Network", responseObject = NetworkResponse.class, responseView = ResponseView.Full, entityType = {Network.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateNetworkCmdByAdmin extends CreateNetworkCmd implements AdminCmd { - @Parameter(name=ApiConstants.VLAN, type=CommandType.STRING, description="the ID or VID of the network") + @Parameter(name=ApiConstants.VLAN, type=CommandType.STRING, description = "The ID or VID of the network") private String vlan; - @Parameter(name=ApiConstants.BYPASS_VLAN_OVERLAP_CHECK, type=CommandType.BOOLEAN, description="when true bypasses VLAN id/range overlap check during network creation for shared and L2 networks") + @Parameter(name=ApiConstants.BYPASS_VLAN_OVERLAP_CHECK, type=CommandType.BOOLEAN, description = "When true bypasses VLAN ID/range overlap check during Network creation for shared and L2 Networks") private Boolean bypassVlanOverlapCheck; - @Parameter(name=ApiConstants.HIDE_IP_ADDRESS_USAGE, type=CommandType.BOOLEAN, description="when true ip address usage for the network will not be exported by the listUsageRecords API") + @Parameter(name=ApiConstants.HIDE_IP_ADDRESS_USAGE, type=CommandType.BOOLEAN, description = "When true IP address usage for the Network will not be exported by the listUsageRecords API") private Boolean hideIpAddressUsage; - @Parameter(name = ApiConstants.ROUTER_IP, type = CommandType.STRING, description = "IPV4 address to be assigned to a router in a shared network", since = "4.16", + @Parameter(name = ApiConstants.ROUTER_IP, type = CommandType.STRING, description = "IPv4 address to be assigned to a router in a shared Network", since = "4.16", validations = {ApiArgValidator.NotNullOrEmpty}) private String routerIp; - @Parameter(name = ApiConstants.ROUTER_IPV6, type = CommandType.STRING, description = "IPV6 address to be assigned to a router in a shared network", since = "4.16", + @Parameter(name = ApiConstants.ROUTER_IPV6, type = CommandType.STRING, description = "IPv6 address to be assigned to a router in a shared Network", since = "4.16", validations = {ApiArgValidator.NotNullOrEmpty}) private String routerIpv6; @@ -56,7 +56,7 @@ public class CreateNetworkCmdByAdmin extends CreateNetworkCmd implements AdminCm type = CommandType.LIST, collectionType = CommandType.UUID, entityType = BgpPeerResponse.class, - description = "Ids of the Bgp Peer for the network", + description = "IDs of the Bgp Peer for the Network", since = "4.20.0") private List bgpPeerIds; 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 51ba6ec02eaf..5c39060f9fa3 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 @@ -16,501 +16,47 @@ // under the License. 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; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import com.cloud.network.Network; -import com.cloud.network.VirtualRouterProvider; -import org.apache.cloudstack.api.response.DomainResponse; -import org.apache.cloudstack.api.response.ZoneResponse; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang3.BooleanUtils; -import org.apache.commons.lang3.StringUtils; - 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.NetworkOfferingResponse; -import org.apache.cloudstack.api.response.ServiceOfferingResponse; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.network.Network.Capability; -import com.cloud.network.Network.Service; import com.cloud.offering.NetworkOffering; -import com.cloud.offering.NetworkOffering.Availability; -import com.cloud.user.Account; - -import static com.cloud.network.Network.Service.Dhcp; -import static com.cloud.network.Network.Service.Dns; -import static com.cloud.network.Network.Service.Lb; -import static com.cloud.network.Network.Service.StaticNat; -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.Firewall; @APICommand(name = "createNetworkOffering", description = "Creates a network offering.", responseObject = NetworkOfferingResponse.class, since = "3.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class CreateNetworkOfferingCmd extends BaseCmd { +public class CreateNetworkOfferingCmd extends NetworkOfferingBaseCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the network offering") - private String networkOfferingName; - - @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "the display text of the network offering, defaults to the value of 'name'.") - private String displayText; - @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, required = true, - description = "the traffic type for the network offering. Supported type in current release is GUEST only") + description = "The traffic type for the network offering. Supported type in current release is GUEST only") private String traffictype; - @Parameter(name = ApiConstants.TAGS, type = CommandType.STRING, description = "the tags for the network offering.", length = 4096) - private String tags; - - @Parameter(name = ApiConstants.SPECIFY_VLAN, type = CommandType.BOOLEAN, description = "true if network offering supports vlans") - private Boolean specifyVlan; - - @Parameter(name = ApiConstants.AVAILABILITY, type = CommandType.STRING, description = "the availability of network offering. The default value is Optional. " - + " Another value is Required, which will make it as the default network offering for new networks ") - private String availability; - - @Parameter(name = ApiConstants.NETWORKRATE, type = CommandType.INTEGER, description = "data transfer rate in megabits per second allowed") - private Integer networkRate; - - @Parameter(name = ApiConstants.CONSERVE_MODE, type = CommandType.BOOLEAN, description = "true if the network offering is IP conserve mode enabled") - private Boolean conserveMode; - - @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, - type = CommandType.UUID, - entityType = ServiceOfferingResponse.class, - description = "the service offering ID used by virtual router provider") - private Long serviceOfferingId; - - @Parameter(name = ApiConstants.GUEST_IP_TYPE, type = CommandType.STRING, required = true, description = "guest type of the network offering: Shared or Isolated") + @Parameter(name = ApiConstants.GUEST_IP_TYPE, type = CommandType.STRING, required = true, description = "Guest type of the network offering: Shared or Isolated") private String guestIptype; - @Parameter(name = ApiConstants.INTERNET_PROTOCOL, - type = CommandType.STRING, - description = "The internet protocol of network offering. Options are ipv4 and dualstack. Default is ipv4. dualstack will create a network offering that supports both IPv4 and IPv6", - since = "4.17.0") - private String internetProtocol; - - @Parameter(name = ApiConstants.SUPPORTED_SERVICES, - type = CommandType.LIST, - collectionType = CommandType.STRING, - description = "services supported by the network offering") - private List supportedServices; - - @Parameter(name = ApiConstants.SERVICE_PROVIDER_LIST, - type = CommandType.MAP, - description = "provider to service mapping. If not specified, the provider for the service will be mapped to the default provider on the physical network") - private Map serviceProviderList; - - @Parameter(name = ApiConstants.SERVICE_CAPABILITY_LIST, type = CommandType.MAP, description = "desired service capabilities as part of network offering") - private Map serviceCapabilitystList; - - @Parameter(name = ApiConstants.SPECIFY_IP_RANGES, - type = CommandType.BOOLEAN, - description = "true if network offering supports specifying ip ranges; defaulted to false if not specified") - private Boolean specifyIpRanges; - - @Parameter(name = ApiConstants.IS_PERSISTENT, - type = CommandType.BOOLEAN, - description = "true if network offering supports persistent networks; defaulted to false if not specified") - private Boolean isPersistent; - - @Parameter(name = ApiConstants.FOR_VPC, - type = CommandType.BOOLEAN, - 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.", - since = "4.20.0") - private Boolean nsxSupportsLbService; - - @Parameter(name = ApiConstants.NSX_SUPPORTS_INTERNAL_LB, - type = CommandType.BOOLEAN, - description = "true if network offering for NSX network offering supports Internal Load balancer service.", - since = "4.20.0") - private Boolean nsxSupportsInternalLbService; - - @Parameter(name = ApiConstants.NETWORK_MODE, - type = CommandType.STRING, - description = "Indicates the mode with which the network will operate. Valid option: NATTED or ROUTED", - since = "4.20.0") - private String networkMode; - - @Parameter(name = ApiConstants.FOR_TUNGSTEN, - type = CommandType.BOOLEAN, - description = "true if network offering is meant to be used for Tungsten-Fabric, false otherwise.") - private Boolean forTungsten; - - @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, since = "4.2.0", description = "Network offering details in key/value pairs." - + " Supported keys are internallbprovider/publiclbprovider with service provider as a value, and" - + " promiscuousmode/macaddresschanges/forgedtransmits with true/false as value to accept/reject the security settings if available for a nic/portgroup") - protected Map details; - - @Parameter(name = ApiConstants.EGRESS_DEFAULT_POLICY, - type = CommandType.BOOLEAN, - description = "true if guest network default egress policy is allow; false if default egress policy is deny") - private Boolean egressDefaultPolicy; - - @Parameter(name = ApiConstants.KEEPALIVE_ENABLED, - type = CommandType.BOOLEAN, - required = false, - description = "if true keepalive will be turned on in the loadbalancer. At the time of writing this has only an effect on haproxy; the mode http and httpclose options are unset in the haproxy conf file.") - private Boolean keepAliveEnabled; - - @Parameter(name = ApiConstants.MAX_CONNECTIONS, - type = CommandType.INTEGER, - description = "maximum number of concurrent connections supported by the network offering") - private Integer maxConnections; - - @Parameter(name = ApiConstants.DOMAIN_ID, - type = CommandType.LIST, - collectionType = CommandType.UUID, - entityType = DomainResponse.class, - description = "the ID of the containing domain(s), null for public offerings") - private List domainIds; - - @Parameter(name = ApiConstants.ZONE_ID, - type = CommandType.LIST, - collectionType = CommandType.UUID, - entityType = ZoneResponse.class, - description = "the ID of the containing zone(s), null for public offerings", - since = "4.13") - private List zoneIds; - - @Parameter(name = ApiConstants.ENABLE, - type = CommandType.BOOLEAN, - description = "set to true if the offering is to be enabled during creation. Default is false", - since = "4.16") - private Boolean enable; - - @Parameter(name = ApiConstants.SPECIFY_AS_NUMBER, type = CommandType.BOOLEAN, since = "4.20.0", - description = "true if network offering supports choosing AS number") - private Boolean specifyAsNumber; - - @Parameter(name = ApiConstants.ROUTING_MODE, - type = CommandType.STRING, - since = "4.20.0", - description = "the routing mode for the network offering. Supported types are: Static or Dynamic.") - private String routingMode; - ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - public String getNetworkOfferingName() { - return networkOfferingName; - } - - public String getDisplayText() { - return StringUtils.isEmpty(displayText) ? networkOfferingName : displayText; - } - - public String getTags() { - return tags; - } - public String getTraffictype() { return traffictype; } - public Boolean getSpecifyVlan() { - return specifyVlan == null ? false : specifyVlan; - } - - public String getAvailability() { - return availability == null ? Availability.Optional.toString() : availability; - } - - public Integer getNetworkRate() { - return networkRate; - } - - public Long getServiceOfferingId() { - 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 (!isExternalNetworkProvider()) { - return supportedServices == null ? new ArrayList() : supportedServices; - } else { - List services = new ArrayList<>(List.of( - Dhcp.getName(), - Dns.getName(), - UserData.getName() - )); - if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode())) { - services.addAll(Arrays.asList( - StaticNat.getName(), - SourceNat.getName(), - PortForwarding.getName())); - } - if (getNsxSupportsLbService()) { - services.add(Lb.getName()); - } - if (Boolean.TRUE.equals(forVpc)) { - services.add(NetworkACL.getName()); - } else { - services.add(Firewall.getName()); - } - return services; - } - } - public String getGuestIpType() { return guestIptype; } - public String getInternetProtocol() { - return internetProtocol; - } - - public Boolean getSpecifyIpRanges() { - return specifyIpRanges == null ? false : specifyIpRanges; - } - - public Boolean getConserveMode() { - if (conserveMode == null) { - return true; - } - return conserveMode; - } - - public Boolean getIsPersistent() { - return isPersistent == null ? false : isPersistent; - } - - public Boolean getForVpc() { - return forVpc; - } - - public String getNetworkMode() { - return networkMode; - } - - public boolean getNsxSupportsLbService() { - return BooleanUtils.isTrue(nsxSupportsLbService); - } - - public boolean getNsxSupportsInternalLbService() { - return BooleanUtils.isTrue(nsxSupportsInternalLbService); - } - - public Boolean getForTungsten() { - return forTungsten; - } - - public Boolean getEgressDefaultPolicy() { - if (egressDefaultPolicy == null) { - return true; - } - return egressDefaultPolicy; - } - - public Boolean getKeepAliveEnabled() { - return keepAliveEnabled; - } - - public Integer getMaxconnections() { - return maxConnections; - } - - public Map> getServiceProviders() { - Map> serviceProviderMap = new HashMap<>(); - if (serviceProviderList != null && !serviceProviderList.isEmpty() && !isExternalNetworkProvider()) { - Collection servicesCollection = serviceProviderList.values(); - Iterator iter = servicesCollection.iterator(); - while (iter.hasNext()) { - HashMap services = (HashMap) iter.next(); - String service = services.get("service"); - String provider = services.get("provider"); - List providerList = null; - if (serviceProviderMap.containsKey(service)) { - providerList = serviceProviderMap.get(service); - } else { - providerList = new ArrayList(); - } - providerList.add(provider); - serviceProviderMap.put(service, providerList); - } - } else if (isExternalNetworkProvider()) { - getServiceProviderMapForExternalProvider(serviceProviderMap, Network.Provider.getProvider(provider).getName()); - } - return 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", "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())) { - unsupportedServices.add("Firewall"); - } else { - unsupportedServices.add("NetworkACL"); - } - for (String service : allServices) { - if (unsupportedServices.contains(service)) - continue; - if (routerSupported.contains(service)) - serviceProviderMap.put(service, List.of(routerProvider)); - else if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode()) || NetworkACL.getName().equalsIgnoreCase(service)) { - serviceProviderMap.put(service, List.of(provider)); - } - if (!getNsxSupportsLbService()) { - serviceProviderMap.remove(Lb.getName()); - } - } - } - - public Map getServiceCapabilities(Service service) { - Map capabilityMap = null; - - if (serviceCapabilitystList != null && !serviceCapabilitystList.isEmpty()) { - capabilityMap = new HashMap(); - Collection serviceCapabilityCollection = serviceCapabilitystList.values(); - Iterator iter = serviceCapabilityCollection.iterator(); - while (iter.hasNext()) { - HashMap svcCapabilityMap = (HashMap) iter.next(); - Capability capability = null; - String svc = svcCapabilityMap.get("service"); - String capabilityName = svcCapabilityMap.get("capabilitytype"); - String capabilityValue = svcCapabilityMap.get("capabilityvalue"); - - if (capabilityName != null) { - capability = Capability.getCapability(capabilityName); - } - - if ((capability == null) || (capabilityName == null) || (capabilityValue == null)) { - throw new InvalidParameterValueException("Invalid capability:" + capabilityName + " capability value:" + capabilityValue); - } - - if (svc.equalsIgnoreCase(service.getName())) { - capabilityMap.put(capability, capabilityValue); - } else { - //throw new InvalidParameterValueException("Service is not equal ") - } - } - } - - return capabilityMap; - } - - public Map getDetails() { - if (details == null || details.isEmpty()) { - return null; - } - - Collection paramsCollection = details.values(); - Object objlist[] = paramsCollection.toArray(); - Map params = (Map) (objlist[0]); - for (int i = 1; i < objlist.length; i++) { - params.putAll((Map) (objlist[i])); - } - - return params; - } - - public String getServicePackageId() { - Map data = getDetails(); - if (data == null) - return null; - return data.get(NetworkOffering.Detail.servicepackageuuid + ""); - } - - public List getDomainIds() { - if (CollectionUtils.isNotEmpty(domainIds)) { - Set set = new LinkedHashSet<>(domainIds); - domainIds.clear(); - domainIds.addAll(set); - } - return domainIds; - } - - public List getZoneIds() { - if (CollectionUtils.isNotEmpty(zoneIds)) { - Set set = new LinkedHashSet<>(zoneIds); - zoneIds.clear(); - zoneIds.addAll(set); - } - return zoneIds; - } - - public Boolean getEnable() { - if (enable != null) { - return enable; - } - return false; - } - - public boolean getSpecifyAsNumber() { - return BooleanUtils.toBoolean(specifyAsNumber); - } - - public String getRoutingMode() { - return routingMode; - } - ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// - @Override - public long getEntityOwnerId() { - return Account.ACCOUNT_ID_SYSTEM; - } @Override public void execute() { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreatePhysicalNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreatePhysicalNetworkCmd.java index 7eb52b92456c..097b8a5b5458 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreatePhysicalNetworkCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreatePhysicalNetworkCmd.java @@ -36,7 +36,7 @@ import com.cloud.network.PhysicalNetwork; import com.cloud.user.Account; -@APICommand(name = "createPhysicalNetwork", description = "Creates a physical network", responseObject = PhysicalNetworkResponse.class, since = "3.0.0", +@APICommand(name = "createPhysicalNetwork", description = "Creates a physical Network", responseObject = PhysicalNetworkResponse.class, since = "3.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreatePhysicalNetworkCmd extends BaseAsyncCreateCmd { @@ -49,36 +49,36 @@ public class CreatePhysicalNetworkCmd extends BaseAsyncCreateCmd { type = CommandType.UUID, entityType = ZoneResponse.class, required = true, - description = "the Zone ID for the physical network") + description = "The Zone ID for the physical Network") private Long zoneId; - @Parameter(name = ApiConstants.VLAN, type = CommandType.STRING, description = "the VLAN for the physical network") + @Parameter(name = ApiConstants.VLAN, type = CommandType.STRING, description = "The VLAN for the physical Network") private String vlan; - @Parameter(name = ApiConstants.NETWORK_SPEED, type = CommandType.STRING, description = "the speed for the physical network[1G/10G]") + @Parameter(name = ApiConstants.NETWORK_SPEED, type = CommandType.STRING, description = "The speed for the physical Network[1G/10G]") private String speed; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "domain ID of the account owning a physical network") + description = "Domain ID of the Account owning a physical Network") private Long domainId; @Parameter(name = ApiConstants.BROADCAST_DOMAIN_RANGE, type = CommandType.STRING, - description = "the broadcast domain range for the physical network[Pod or Zone]. In Acton release it can be Zone only in Advance zone, and Pod in Basic") + description = "The broadcast domain range for the physical Network[Pod or Zone]. In Acton release it can be Zone only in Advance zone, and Pod in Basic") private String broadcastDomainRange; - @Parameter(name = ApiConstants.TAGS, type = CommandType.LIST, collectionType = CommandType.STRING, description = "Tag the physical network") + @Parameter(name = ApiConstants.TAGS, type = CommandType.LIST, collectionType = CommandType.STRING, description = "Tag the physical Network") private List tags; @Parameter(name = ApiConstants.ISOLATION_METHODS, type = CommandType.LIST, collectionType = CommandType.STRING, - description = "the isolation method for the physical network[VLAN/L3/GRE]") + description = "The isolation method for the physical Network[VLAN/VXLAN/GRE/STT/BCF_SEGMENT/SSP/ODL/L3VPN/VCS/NSX/NETRIS]") private List isolationMethods; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the physical network") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "The name of the physical Network") private String networkName; ///////////////////////////////////////////////////// @@ -139,7 +139,7 @@ public String getCreateEventDescription() { @Override public String getEventDescription() { - return "creating Physical Network. Id: " + getEntityId(); + return "Creating Physical Network. ID: " + getEntityId(); } ///////////////////////////////////////////////////// @@ -148,14 +148,14 @@ public String getEventDescription() { @Override public void execute() { - CallContext.current().setEventDetails("Physical Network Id: " + getEntityId()); + CallContext.current().setEventDetails("Physical Network ID: " + getEntityUuid()); PhysicalNetwork result = _networkService.getCreatedPhysicalNetwork(getEntityId()); if (result != null) { PhysicalNetworkResponse response = _responseGenerator.createPhysicalNetworkResponse(result); response.setResponseName(getCommandName()); this.setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create physical network"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create physical Network"); } } @@ -168,7 +168,7 @@ public void create() throws ResourceAllocationException { setEntityId(result.getId()); setEntityUuid(result.getUuid()); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create physical network entity"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create physical Network entity"); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateStorageNetworkIpRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateStorageNetworkIpRangeCmd.java index 42262cc2bf15..439edcbf8f99 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateStorageNetworkIpRangeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateStorageNetworkIpRangeCmd.java @@ -50,24 +50,24 @@ public class CreateStorageNetworkIpRangeCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = PodResponse.class, required = true, - description = "UUID of pod where the ip range belongs to") + description = "ID of pod where the IP range belongs to") private Long podId; - @Parameter(name = ApiConstants.START_IP, type = CommandType.STRING, required = true, description = "the beginning IP address") + @Parameter(name = ApiConstants.START_IP, type = CommandType.STRING, required = true, description = "The starting IP address") private String startIp; - @Parameter(name = ApiConstants.END_IP, type = CommandType.STRING, description = "the ending IP address") + @Parameter(name = ApiConstants.END_IP, type = CommandType.STRING, description = "The ending IP address") private String endIp; @Parameter(name = ApiConstants.VLAN, type = CommandType.INTEGER, - description = "Optional. The vlan the ip range sits on, default to Null when it is not specified which means your network is not on any Vlan. This is mainly for Vmware as other hypervisors can directly retrieve bridge from physical network traffic type table") + description = "Optional. The VLAN the IP range sits on, default to Null when it is not specified which means your network is not on any VLAN. This is mainly for VMware as other hypervisors can directly retrieve bridge from physical network traffic type table") private Integer vlan; - @Parameter(name = ApiConstants.NETMASK, type = CommandType.STRING, required = true, description = "the netmask for storage network") + @Parameter(name = ApiConstants.NETMASK, type = CommandType.STRING, required = true, description = "The netmask for storage network") private String netmask; - @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, required = true, description = "the gateway for storage network") + @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, required = true, description = "The gateway for storage network") private String gateway; ///////////////////////////////////////////////////// @@ -105,7 +105,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Creating storage ip range from " + getStartIp() + " to " + getEndIp() + " with vlan " + getVlan(); + return "Creating storage IP range from " + getStartIp() + " to " + getEndIp() + " with VLAN " + getVlan(); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DedicateGuestVlanRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DedicateGuestVlanRangeCmd.java index 355f738679e0..dcc1fa51dcee 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DedicateGuestVlanRangeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DedicateGuestVlanRangeCmd.java @@ -44,23 +44,23 @@ public class DedicateGuestVlanRangeCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.VLAN_RANGE, type = CommandType.STRING, required = true, description = "guest vlan range to be dedicated") + @Parameter(name = ApiConstants.VLAN_RANGE, type = CommandType.STRING, required = true, description = "Guest VLAN range to be dedicated") private String vlan; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "account who will own the VLAN") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "Account who will own the VLAN") private String accountName; - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "project who will own the VLAN") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Project who will own the VLAN") private Long projectId; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "domain ID of the account owning a VLAN") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "Domain ID of the Account owning a VLAN") private Long domainId; @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, required = true, - description = "physical network ID of the vlan") + description = "Physical Network ID of the VLAN") private Long physicalNetworkId; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DedicateIpv4SubnetForZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DedicateIpv4SubnetForZoneCmd.java index 2df032c559c5..cc76b284e24a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DedicateIpv4SubnetForZoneCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DedicateIpv4SubnetForZoneCmd.java @@ -82,7 +82,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Dedicating zone IPv4 subnet " + getId(); + return "Dedicating zone's IPv4 subnet with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteGuestNetworkIpv6PrefixCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteGuestNetworkIpv6PrefixCmd.java index e2ada4191a82..405bbb594edb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteGuestNetworkIpv6PrefixCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteGuestNetworkIpv6PrefixCmd.java @@ -63,7 +63,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Deleting guest IPv6 prefix " + getId(); + return "Deleting guest IPv6 prefix with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForGuestNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForGuestNetworkCmd.java index 28a646f9d036..f6b22f79dfc7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForGuestNetworkCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForGuestNetworkCmd.java @@ -59,7 +59,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Deleting guest IPv4 subnet " + getId(); + return "Deleting guest IPv4 subnet with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForZoneCmd.java index 222bc1bad98d..0ff2a9ad70b8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForZoneCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForZoneCmd.java @@ -59,7 +59,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Deleting zone IPv4 subnet " + getId(); + return "Deleting zone IPv4 subnet with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteManagementNetworkIpRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteManagementNetworkIpRangeCmd.java index 41cf5e518b34..1e69aaa6c440 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteManagementNetworkIpRangeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteManagementNetworkIpRangeCmd.java @@ -100,7 +100,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Deleting management ip range from " + getStartIp() + " to " + getEndIp() + " of Pod: " + getPodId(); + return "Deleting management IP range from " + getStartIp() + " to " + getEndIp() + " from Pod: " + getResourceUuid(ApiConstants.POD_ID); } @Override @@ -116,7 +116,7 @@ public void execute() { logger.warn("Exception: ", ex); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); } catch (Exception e) { - logger.warn("Failed to delete management ip range from " + getStartIp() + " to " + getEndIp() + " of Pod: " + getPodId(), e); + logger.warn("Failed to delete management ip range from {} to {} of Pod: {}", getStartIp(), getEndIp(), getPodId(), e); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteNetworkOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteNetworkOfferingCmd.java index e0598b71ea17..a24fc0dc3714 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteNetworkOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteNetworkOfferingCmd.java @@ -40,7 +40,7 @@ public class DeleteNetworkOfferingCmd extends BaseCmd { type = CommandType.UUID, entityType = NetworkOfferingResponse.class, required = true, - description = "the ID of the network offering") + description = "The ID of the network offering") private Long id; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteNetworkServiceProviderCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteNetworkServiceProviderCmd.java index 4b56612fddaa..2573e92b9860 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteNetworkServiceProviderCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteNetworkServiceProviderCmd.java @@ -45,7 +45,7 @@ public class DeleteNetworkServiceProviderCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = ProviderResponse.class, required = true, - description = "the ID of the network service provider") + description = "The ID of the network service provider") private Long id; ///////////////////////////////////////////////////// @@ -91,7 +91,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Deleting Physical network ServiceProvider: " + getId(); + return "Deleting Physical network ServiceProvider with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeletePhysicalNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeletePhysicalNetworkCmd.java index 3233130211c4..9994e8e391d7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeletePhysicalNetworkCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeletePhysicalNetworkCmd.java @@ -43,7 +43,7 @@ public class DeletePhysicalNetworkCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, required = true, - description = "the ID of the Physical network") + description = "The ID of the Physical network") private Long id; ///////////////////////////////////////////////////// @@ -65,7 +65,7 @@ public long getEntityOwnerId() { @Override public void execute() { - CallContext.current().setEventDetails("Physical Network Id: " + id); + CallContext.current().setEventDetails("Physical Network Id: " + getResourceUuid(ApiConstants.ID)); boolean result = _networkService.deletePhysicalNetwork(getId()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); @@ -77,7 +77,7 @@ public void execute() { @Override public String getEventDescription() { - return "Deleting Physical network: " + getId(); + return "Deleting Physical network with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteStorageNetworkIpRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteStorageNetworkIpRangeCmd.java index 454dfba92f20..dcab38561408 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteStorageNetworkIpRangeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteStorageNetworkIpRangeCmd.java @@ -46,7 +46,7 @@ public class DeleteStorageNetworkIpRangeCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = StorageNetworkIpRangeResponse.class, required = true, - description = "the uuid of the storage network ip range") + description = "The UUID of the storage network IP range") private Long id; ///////////////////////////////////////////////////// @@ -64,7 +64,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Deleting storage ip range " + getId(); + return "Deleting storage IP range with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -75,7 +75,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); } catch (Exception e) { - logger.warn("Failed to delete storage network ip range " + getId(), e); + logger.warn("Failed to delete storage network ip range {}", getId(), e); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListDedicatedGuestVlanRangesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListDedicatedGuestVlanRangesCmd.java index 0247a3069212..a5edbfdce4a9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListDedicatedGuestVlanRangesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListDedicatedGuestVlanRangesCmd.java @@ -44,33 +44,33 @@ public class ListDedicatedGuestVlanRangesCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestVlanRangeResponse.class, description = "list dedicated guest vlan ranges by id") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestVlanRangeResponse.class, description = "List dedicated guest VLAN ranges by ID") private Long id; @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, - description = "the account with which the guest VLAN range is associated. Must be used with the domainId parameter.") + description = "The account with which the guest VLAN range is associated. Must be used with the domainId parameter.") private String accountName; - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "project who will own the guest VLAN range") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Project who will own the guest VLAN range") private Long projectId; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "the domain ID with which the guest VLAN range is associated. If used with the account parameter, returns all guest VLAN ranges for that account in the specified domain.") + description = "The domain ID with which the guest VLAN range is associated. If used with the account parameter, returns all guest VLAN ranges for that account in the specified domain.") private Long domainId; - @Parameter(name = ApiConstants.GUEST_VLAN_RANGE, type = CommandType.STRING, description = "the dedicated guest vlan range") + @Parameter(name = ApiConstants.GUEST_VLAN_RANGE, type = CommandType.STRING, description = "The dedicated guest vlan range") private String guestVlanRange; @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, - description = "physical network id of the guest VLAN range") + description = "Physical network ID of the guest VLAN range") private Long physicalNetworkId; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "zone of the guest VLAN range") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "Zone of the guest VLAN range") private Long zoneId; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListGuestVlansCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListGuestVlansCmd.java index 4b368f5e0341..80c9540a8486 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListGuestVlansCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListGuestVlansCmd.java @@ -44,19 +44,19 @@ public class ListGuestVlansCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.LONG, required = false, description = "list guest vlan by id") + @Parameter(name = ApiConstants.ID, type = CommandType.LONG, required = false, description = "List guest VLAN by ID") private Long id; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = false, description = "list guest vlan by zone") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = false, description = "List guest VLAN by zone") private Long zoneId; - @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, required = false, description = "list guest vlan by physical network") + @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, required = false, description = "List guest VLAN by physical network") private Long physicalNetworkId; - @Parameter(name = ApiConstants.VNET, type = CommandType.STRING, required = false, description = "list guest vlan by vnet") + @Parameter(name = ApiConstants.VNET, type = CommandType.STRING, required = false, description = "List guest VLAN by vnet") private String vnet; - @Parameter(name = ApiConstants.ALLOCATED_ONLY, type = CommandType.BOOLEAN, required = false, description = "limits search results to allocated guest vlan. false by default.") + @Parameter(name = ApiConstants.ALLOCATED_ONLY, type = CommandType.BOOLEAN, required = false, description = "Limits search results to allocated guest VLAN. False by default.") private Boolean allocatedOnly; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListNetworkDeviceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListNetworkDeviceCmd.java index 768bab641087..ace635376eb4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListNetworkDeviceCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListNetworkDeviceCmd.java @@ -57,7 +57,7 @@ public class ListNetworkDeviceCmd extends BaseListCmd { description = "Network device type, now supports ExternalDhcp, PxeServer, NetscalerMPXLoadBalancer, NetscalerVPXLoadBalancer, NetscalerSDXLoadBalancer, F5BigIpLoadBalancer, JuniperSRXFirewall, PaloAltoFirewall") private String type; - @Parameter(name = ApiConstants.NETWORK_DEVICE_PARAMETER_LIST, type = CommandType.MAP, description = "parameters for network device") + @Parameter(name = ApiConstants.NETWORK_DEVICE_PARAMETER_LIST, type = CommandType.MAP, description = "Parameters for network device") private Map paramList; public String getDeviceType() { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListNetworkServiceProvidersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListNetworkServiceProvidersCmd.java index 68495a62215f..95071e41f6c0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListNetworkServiceProvidersCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListNetworkServiceProvidersCmd.java @@ -44,13 +44,13 @@ public class ListNetworkServiceProvidersCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, description = "the Physical Network ID") + @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, description = "The Physical Network ID") private Long physicalNetworkId; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "list providers by name") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "List providers by name") private String name; - @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "list providers by state") + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "List providers by state") private String state; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListNetworksCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListNetworksCmdByAdmin.java index 8df1133d5e7c..bb564f1c3ded 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListNetworksCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListNetworksCmdByAdmin.java @@ -30,7 +30,7 @@ requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListNetworksCmdByAdmin extends ListNetworksCmd implements AdminCmd { - @Parameter(name= ApiConstants.VLAN, type=CommandType.STRING, description="the ID or VID of the network", since = "4.17.0") + @Parameter(name= ApiConstants.VLAN, type=CommandType.STRING, description = "The ID or VID of the network", since = "4.17.0") private String vlan; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListPhysicalNetworksCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListPhysicalNetworksCmd.java index 51a6ddabd9f1..27319ff3d671 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListPhysicalNetworksCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListPhysicalNetworksCmd.java @@ -43,13 +43,13 @@ public class ListPhysicalNetworksCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, description = "list physical network by id") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, description = "List physical network by id") private Long id; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the Zone ID for the physical network") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The Zone ID for the physical network") private Long zoneId; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "search by name") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Search by name") private String networkName; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListStorageNetworkIpRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListStorageNetworkIpRangeCmd.java index 556162ca360d..3e32bed3d500 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListStorageNetworkIpRangeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListStorageNetworkIpRangeCmd.java @@ -51,19 +51,19 @@ public class ListStorageNetworkIpRangeCmd extends BaseListCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = StorageNetworkIpRangeResponse.class, - description = "optional parameter. Storaget network IP range uuid, if specicied, using it to search the range.") + description = "Optional parameter. Storage network IP range UUID, if specified, using it to search the range.") private Long rangeId; @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, - description = "optional parameter. Pod uuid, if specicied and range uuid is absent, using it to search the range.") + description = "Optional parameter. Pod UUID, if specified and range UUID is absent, using it to search the range.") private Long podId; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, - description = "optional parameter. Zone uuid, if specicied and both pod uuid and range uuid are absent, using it to search the range.") + description = "Optional parameter. Zone UUID, if specified and both pod UUID and range UUID are absent, using it to search the range.") private Long zoneId; ///////////////////////////////////////////////////// @@ -97,7 +97,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE response.setResponseName(getCommandName()); this.setResponseObject(response); } catch (Exception e) { - logger.warn("Failed to list storage network ip range for rangeId=" + getRangeId() + " podId=" + getPodId() + " zoneId=" + getZoneId()); + logger.warn("Failed to list storage Network IP range for rangeId={} podId={} zoneId={}", getRangeId(), getPodId(), getZoneId()); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListSupportedNetworkServicesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListSupportedNetworkServicesCmd.java index 120c6af41ad6..227e9b684526 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListSupportedNetworkServicesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListSupportedNetworkServicesCmd.java @@ -40,10 +40,10 @@ responseHasSensitiveInfo = false) public class ListSupportedNetworkServicesCmd extends BaseListCmd { - @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, description = "network service provider name") + @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, description = "Network service provider name") private String providerName; - @Parameter(name = ApiConstants.SERVICE, type = CommandType.STRING, description = "network service name to list providers and capabilities of") + @Parameter(name = ApiConstants.SERVICE, type = CommandType.STRING, description = "Network service name to list providers and capabilities of") private String serviceName; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/MigrateNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/MigrateNetworkCmd.java index 8ef853b99da8..ad78bd3b406c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/MigrateNetworkCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/MigrateNetworkCmd.java @@ -38,7 +38,7 @@ import com.cloud.user.Account; import com.cloud.user.User; -@APICommand(name = "migrateNetwork", description = "moves a network to another physical network", +@APICommand(name = "migrateNetwork", description = "Moves a network to another physical network", responseObject = NetworkResponse.class, responseView = ResponseView.Restricted, entityType = {Network.class}, @@ -53,13 +53,13 @@ public class MigrateNetworkCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) - @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "the ID of the network") + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "The ID of the network") protected Long id; - @Parameter(name = ApiConstants.NETWORK_OFFERING_ID, type = CommandType.UUID, entityType = NetworkOfferingResponse.class, required = true, description = "network offering ID") + @Parameter(name = ApiConstants.NETWORK_OFFERING_ID, type = CommandType.UUID, entityType = NetworkOfferingResponse.class, required = true, description = "Network offering ID") private Long networkOfferingId; - @Parameter(name = ApiConstants.RESUME, type = CommandType.BOOLEAN, description = "true if previous network migration cmd failed") + @Parameter(name = ApiConstants.RESUME, type = CommandType.BOOLEAN, description = "True if previous network migration cmd failed") private Boolean resume; ///////////////////////////////////////////////////// @@ -115,7 +115,7 @@ public void execute() { @Override public String getEventDescription() { - StringBuilder eventMsg = new StringBuilder("Migrating network: " + getId()); + String description = "Migrating Network with ID: " + getResourceUuid(ApiConstants.NETWORK_ID); if (getNetworkOfferingId() != null) { Network network = _networkService.getNetwork(getId()); if (network == null) { @@ -128,11 +128,11 @@ public String getEventDescription() { throw new InvalidParameterValueException("Network offering id supplied is invalid"); } - eventMsg.append(". Original network offering id: " + oldOff.getUuid() + ", new network offering id: " + newOff.getUuid()); + description += ". Original Network Offering id: " + oldOff.getUuid() + ", new Network Offering id: " + newOff.getUuid(); } } - return eventMsg.toString(); + return description; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/MigrateVPCCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/MigrateVPCCmd.java index 3e0801be40b1..2973fea33c6a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/MigrateVPCCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/MigrateVPCCmd.java @@ -40,7 +40,7 @@ import com.cloud.user.User; @APICommand(name = "migrateVPC", - description = "moves a vpc to another physical network", + description = "Moves a VPC to another physical network", responseObject = VpcResponse.class, responseView = ResponseObject.ResponseView.Restricted, entityType = {Vpc.class}, @@ -56,16 +56,16 @@ public class MigrateVPCCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// @ACL(accessType = SecurityChecker.AccessType.OperateEntry) @Parameter(name= ApiConstants.VPC_ID, type=CommandType.UUID, entityType = VpcResponse.class, - required=true, description = "the ID of the vpc") + required=true, description = "The ID of the VPC ") protected Long id; - @Parameter(name = ApiConstants.VPC_OFF_ID, type = CommandType.UUID, entityType = VpcOfferingResponse.class, required=true, description = "vpc offering ID") + @Parameter(name = ApiConstants.VPC_OFF_ID, type = CommandType.UUID, entityType = VpcOfferingResponse.class, required=true, description = "VPC offering ID") private Long vpcOfferingId; - @Parameter(name = ApiConstants.TIER_NETWORK_OFFERINGS, type = CommandType.MAP, description = "network offering ids for each network in the vpc. Example: tierNetworkOfferings[0].networkId=networkId1&tierNetworkOfferings[0].networkOfferingId=newNetworkofferingId1&tierNetworkOfferings[1].networkId=networkId2&tierNetworkOfferings[1].networkOfferingId=newNetworkofferingId2") + @Parameter(name = ApiConstants.TIER_NETWORK_OFFERINGS, type = CommandType.MAP, description = "Network offering IDs for each network in the VPC. Example: tierNetworkOfferings[0].networkId=networkId1&tierNetworkOfferings[0].networkOfferingId=newNetworkofferingId1&tierNetworkOfferings[1].networkId=networkId2&tierNetworkOfferings[1].networkOfferingId=newNetworkofferingId2") private Map> tierNetworkOfferings; - @Parameter(name = ApiConstants.RESUME, type = CommandType.BOOLEAN, description = "true if previous network migration cmd failed") + @Parameter(name = ApiConstants.RESUME, type = CommandType.BOOLEAN, description = "True if previous network migration cmd failed") private Boolean resume; ///////////////////////////////////////////////////// @@ -115,12 +115,12 @@ public void execute() { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to migrate vpc"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to migrate VPC"); } } @Override - public String getEventDescription() { return "Migrating vpc: " + getId() + " to new vpc offering (" + vpcOfferingId + ")"; } + public String getEventDescription() { return "Migrating VPC with ID: " + getResourceUuid(ApiConstants.VPC_ID) + " to new VPC offering with ID: " + getResourceUuid(ApiConstants.VPC_OFF_ID);} @Override public String getEventType() { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/NetworkOfferingBaseCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/NetworkOfferingBaseCmd.java new file mode 100644 index 000000000000..9b42be137314 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/NetworkOfferingBaseCmd.java @@ -0,0 +1,494 @@ +// 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.network; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.Network; +import com.cloud.network.VirtualRouterProvider; +import com.cloud.offering.NetworkOffering; +import com.cloud.user.Account; +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.ServiceOfferingResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.cloud.network.Network.Service.Dhcp; +import static com.cloud.network.Network.Service.Dns; +import static com.cloud.network.Network.Service.Firewall; +import static com.cloud.network.Network.Service.Lb; +import static com.cloud.network.Network.Service.NetworkACL; +import static com.cloud.network.Network.Service.PortForwarding; +import static com.cloud.network.Network.Service.SourceNat; +import static com.cloud.network.Network.Service.StaticNat; +import static com.cloud.network.Network.Service.UserData; + +import static org.apache.cloudstack.api.command.utils.OfferingUtils.isNsxWithoutLb; +import static org.apache.cloudstack.api.command.utils.OfferingUtils.isNetrisNatted; +import static org.apache.cloudstack.api.command.utils.OfferingUtils.isNetrisRouted; + +public abstract class NetworkOfferingBaseCmd extends BaseCmd { + + public abstract String getGuestIpType(); + public abstract String getTraffictype(); + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "The name of the network offering") + private String networkOfferingName; + + @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "The display text of the network offering, defaults to the value of 'name'.") + private String displayText; + + @Parameter(name = ApiConstants.TAGS, type = CommandType.STRING, description = "The tags for the network offering.", length = 4096) + private String tags; + + @Parameter(name = ApiConstants.SPECIFY_VLAN, type = CommandType.BOOLEAN, description = "True if network offering supports VLANs") + private Boolean specifyVlan; + + @Parameter(name = ApiConstants.AVAILABILITY, type = CommandType.STRING, description = "The availability of network offering. The default value is Optional. " + + " Another value is Required, which will make it as the default network offering for new networks ") + private String availability; + + @Parameter(name = ApiConstants.NETWORKRATE, type = CommandType.INTEGER, description = "Data transfer rate in megabits per second allowed") + private Integer networkRate; + + @Parameter(name = ApiConstants.CONSERVE_MODE, type = CommandType.BOOLEAN, description = "True if the network offering is IP conserve mode enabled") + private Boolean conserveMode; + + @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, + type = CommandType.UUID, + entityType = ServiceOfferingResponse.class, + description = "The service offering ID used by virtual router provider") + private Long serviceOfferingId; + + @Parameter(name = ApiConstants.INTERNET_PROTOCOL, + type = CommandType.STRING, + description = "The internet protocol of network offering. Options are IPv4 and dualstack. Default is IPv4. dualstack will create a network offering that supports both IPv4 and IPv6", + since = "4.17.0") + private String internetProtocol; + + @Parameter(name = ApiConstants.SUPPORTED_SERVICES, + type = CommandType.LIST, + collectionType = CommandType.STRING, + description = "Services supported by the network offering") + private List supportedServices; + + @Parameter(name = ApiConstants.SERVICE_PROVIDER_LIST, + type = CommandType.MAP, + description = "Provider to service mapping. If not specified, the provider for the service will be mapped to the default provider on the physical network") + private Map serviceProviderList; + + @Parameter(name = ApiConstants.SERVICE_CAPABILITY_LIST, type = CommandType.MAP, description = "Desired service capabilities as part of network offering") + private Map serviceCapabilitiesList; + + @Parameter(name = ApiConstants.SPECIFY_IP_RANGES, + type = CommandType.BOOLEAN, + description = "True if network offering supports specifying ip ranges; defaulted to false if not specified") + private Boolean specifyIpRanges; + + @Parameter(name = ApiConstants.IS_PERSISTENT, + type = CommandType.BOOLEAN, + description = "True if network offering supports persistent networks; defaulted to false if not specified") + private Boolean isPersistent; + + @Parameter(name = ApiConstants.FOR_VPC, + type = CommandType.BOOLEAN, + 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.", + since = "4.20.0") + private Boolean nsxSupportsLbService; + + @Parameter(name = ApiConstants.NSX_SUPPORTS_INTERNAL_LB, + type = CommandType.BOOLEAN, + description = "True if network offering for NSX network offering supports Internal Load balancer service.", + since = "4.20.0") + private Boolean nsxSupportsInternalLbService; + + @Parameter(name = ApiConstants.NETWORK_MODE, + type = CommandType.STRING, + description = "Indicates the mode with which the network will operate. Valid option: NATTED or ROUTED", + since = "4.20.0") + private String networkMode; + + @Parameter(name = ApiConstants.FOR_TUNGSTEN, + type = CommandType.BOOLEAN, + description = "True if network offering is meant to be used for Tungsten-Fabric, false otherwise.") + private Boolean forTungsten; + + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, since = "4.2.0", description = "Network offering details in key/value pairs." + + " Supported keys are internallbprovider/publiclbprovider with service provider as a value, and" + + " promiscuousmode/macaddresschanges/forgedtransmits with true/false as value to accept/reject the security settings if available for a nic/portgroup") + protected Map details; + + @Parameter(name = ApiConstants.EGRESS_DEFAULT_POLICY, + type = CommandType.BOOLEAN, + description = "True if guest network default egress policy is allow; false if default egress policy is deny") + private Boolean egressDefaultPolicy; + + @Parameter(name = ApiConstants.KEEPALIVE_ENABLED, + type = CommandType.BOOLEAN, + required = false, + description = "If true keepalive will be turned on in the loadbalancer. At the time of writing this has only an effect on haproxy; the mode http and httpclose options are unset in the haproxy conf file.") + private Boolean keepAliveEnabled; + + @Parameter(name = ApiConstants.MAX_CONNECTIONS, + type = CommandType.INTEGER, + description = "Maximum number of concurrent connections supported by the Network offering") + private Integer maxConnections; + + @Parameter(name = ApiConstants.DOMAIN_ID, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = DomainResponse.class, + description = "The ID of the containing domain(s), null for public offerings") + private List domainIds; + + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = ZoneResponse.class, + description = "The ID of the containing zone(s), null for public offerings", + since = "4.13") + private List zoneIds; + + @Parameter(name = ApiConstants.ENABLE, + type = CommandType.BOOLEAN, + description = "Set to true if the offering is to be enabled during creation. Default is false", + since = "4.16") + private Boolean enable; + + @Parameter(name = ApiConstants.SPECIFY_AS_NUMBER, type = CommandType.BOOLEAN, since = "4.20.0", + description = "true if network offering supports choosing AS number") + private Boolean specifyAsNumber; + + @Parameter(name = ApiConstants.ROUTING_MODE, + type = CommandType.STRING, + since = "4.20.0", + description = "the routing mode for the network offering. Supported types are: Static or Dynamic.") + private String routingMode; + + private Map sourceDetailsMap; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getNetworkOfferingName() { + return networkOfferingName; + } + + public String getDisplayText() { + return StringUtils.isEmpty(displayText) ? networkOfferingName : displayText; + } + + public String getTags() { + return tags; + } + + public Boolean getSpecifyVlan() { + return specifyVlan == null ? false : specifyVlan; + } + + public String getAvailability() { + return availability == null ? NetworkOffering.Availability.Optional.toString() : availability; + } + + public Integer getNetworkRate() { + return networkRate; + } + + public Long getServiceOfferingId() { + 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 (!isExternalNetworkProvider()) { + return supportedServices == null ? new ArrayList() : supportedServices; + } else { + List services = new ArrayList<>(List.of( + Dhcp.getName(), + Dns.getName(), + UserData.getName() + )); + 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)) { + services.add(NetworkACL.getName()); + } else { + services.add(Firewall.getName()); + } + return services; + } + } + + public String getInternetProtocol() { + return internetProtocol; + } + + public Boolean getSpecifyIpRanges() { + return specifyIpRanges == null ? false : specifyIpRanges; + } + + public Boolean getConserveMode() { + if (conserveMode == null) { + return true; + } + return conserveMode; + } + + public Boolean getIsPersistent() { + return isPersistent == null ? false : isPersistent; + } + + public Boolean getForVpc() { + return forVpc; + } + + public String getNetworkMode() { + return networkMode; + } + + public boolean getNsxSupportsLbService() { + return BooleanUtils.isTrue(nsxSupportsLbService); + } + + public boolean getNsxSupportsInternalLbService() { + return BooleanUtils.isTrue(nsxSupportsInternalLbService); + } + + public Boolean getForTungsten() { + return forTungsten; + } + + public Boolean getEgressDefaultPolicy() { + if (egressDefaultPolicy == null) { + return true; + } + return egressDefaultPolicy; + } + + public Boolean getKeepAliveEnabled() { + return keepAliveEnabled; + } + + public Integer getMaxconnections() { + return maxConnections; + } + + public Map> getServiceProviders() { + Map> serviceProviderMap = new HashMap<>(); + if (serviceProviderList != null && !serviceProviderList.isEmpty() && !isExternalNetworkProvider()) { + Collection servicesCollection = serviceProviderList.values(); + Iterator iter = servicesCollection.iterator(); + while (iter.hasNext()) { + HashMap services = (HashMap) iter.next(); + String service = services.get("service"); + String provider = services.get("provider"); + List providerList = null; + if (serviceProviderMap.containsKey(service)) { + providerList = serviceProviderMap.get(service); + } else { + providerList = new ArrayList(); + } + providerList.add(provider); + serviceProviderMap.put(service, providerList); + } + } else if (isExternalNetworkProvider()) { + getServiceProviderMapForExternalProvider(serviceProviderMap, Network.Provider.getProvider(provider).getName()); + } + return 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", "Gateway", "SecurityGroup", "Connectivity", "BaremetalPxeService")); + List routerSupported = List.of("Dhcp", "Dns", "UserData"); + List allServices = Network.Service.listAllServices().stream().map(Network.Service::getName).collect(Collectors.toList()); + if (routerProvider.equals(VirtualRouterProvider.Type.VPCVirtualRouter.name())) { + unsupportedServices.add("Firewall"); + } else { + unsupportedServices.add("NetworkACL"); + } + for (String service : allServices) { + if (unsupportedServices.contains(service)) + continue; + if (routerSupported.contains(service)) + serviceProviderMap.put(service, List.of(routerProvider)); + 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()); + } + } + } + + public Map getServiceCapabilities(Network.Service service) { + Map capabilityMap = null; + + if (serviceCapabilitiesList != null && !serviceCapabilitiesList.isEmpty()) { + capabilityMap = new HashMap(); + Collection serviceCapabilityCollection = serviceCapabilitiesList.values(); + Iterator iter = serviceCapabilityCollection.iterator(); + while (iter.hasNext()) { + HashMap svcCapabilityMap = (HashMap) iter.next(); + Network.Capability capability = null; + String svc = svcCapabilityMap.get("service"); + String capabilityName = svcCapabilityMap.get("capabilitytype"); + String capabilityValue = svcCapabilityMap.get("capabilityvalue"); + + if (capabilityName != null) { + capability = Network.Capability.getCapability(capabilityName); + } + + if ((capability == null) || (capabilityName == null) || (capabilityValue == null)) { + throw new InvalidParameterValueException("Invalid capability:" + capabilityName + " capability value:" + capabilityValue); + } + + if (svc.equalsIgnoreCase(service.getName())) { + capabilityMap.put(capability, capabilityValue); + } else { + //throw new InvalidParameterValueException("Service is not equal ") + } + } + } + + return capabilityMap; + } + + public Map getDetails() { + if (details == null || details.isEmpty()) { + return sourceDetailsMap; + } + Collection paramsCollection = details.values(); + Object objlist[] = paramsCollection.toArray(); + Map params = (Map) (objlist[0]); + for (int i = 1; i < objlist.length; i++) { + params.putAll((Map) (objlist[i])); + } + + return params; + } + + public String getServicePackageId() { + Map data = getDetails(); + if (data == null) + return null; + return data.get(NetworkOffering.Detail.servicepackageuuid + ""); + } + + public List getDomainIds() { + if (CollectionUtils.isNotEmpty(domainIds)) { + Set set = new LinkedHashSet<>(domainIds); + domainIds.clear(); + domainIds.addAll(set); + } + return domainIds; + } + + public List getZoneIds() { + if (CollectionUtils.isNotEmpty(zoneIds)) { + Set set = new LinkedHashSet<>(zoneIds); + zoneIds.clear(); + zoneIds.addAll(set); + } + return zoneIds; + } + + public Boolean getEnable() { + if (enable != null) { + return enable; + } + return false; + } + + public boolean getSpecifyAsNumber() { + return BooleanUtils.toBoolean(specifyAsNumber); + } + + public String getRoutingMode() { + return routingMode; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedGuestVlanRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedGuestVlanRangeCmd.java index b3125ec36680..56d042719f68 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedGuestVlanRangeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedGuestVlanRangeCmd.java @@ -44,7 +44,7 @@ public class ReleaseDedicatedGuestVlanRangeCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = GuestVlanRangeResponse.class, required = true, - description = "the ID of the dedicated guest vlan range") + description = "The ID of the dedicated guest VLAN range") private Long id; // /////////////////////////////////////////////////// @@ -72,7 +72,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Releasing a dedicated guest vlan range."; + return "Releasing dedicated guest VLAN range with ID: " + getResourceUuid(ApiConstants.ID); } // /////////////////////////////////////////////////// @@ -81,7 +81,7 @@ public String getEventDescription() { @Override public void execute() { - CallContext.current().setEventDetails("Dedicated guest vlan range Id: " + id); + CallContext.current().setEventDetails("Dedicated guest VLAN range ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _networkService.releaseDedicatedGuestVlanRange(getId()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedIpv4SubnetForZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedIpv4SubnetForZoneCmd.java index 3e151b9b58f4..a5e763c0cb00 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedIpv4SubnetForZoneCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedIpv4SubnetForZoneCmd.java @@ -59,7 +59,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Releasing a dedicated zone IPv4 subnet " + getId(); + return "Releasing dedicated zone IPv4 subnet with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateIpv4SubnetForZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateIpv4SubnetForZoneCmd.java index da7a23f50d9c..db5daa505bee 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateIpv4SubnetForZoneCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateIpv4SubnetForZoneCmd.java @@ -69,7 +69,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Updating zone IPv4 subnet " + getId(); + return "Updating zone IPv4 subnet with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateNetworkCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateNetworkCmdByAdmin.java index b3088a48840e..5879a998b36e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateNetworkCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateNetworkCmdByAdmin.java @@ -29,7 +29,7 @@ @APICommand(name = "updateNetwork", description = "Updates a network", responseObject = NetworkResponse.class, responseView = ResponseView.Full, entityType = {Network.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class UpdateNetworkCmdByAdmin extends UpdateNetworkCmd implements AdminCmd { - @Parameter(name= ApiConstants.HIDE_IP_ADDRESS_USAGE, type=CommandType.BOOLEAN, description="when true ip address usage for the network will not be exported by the listUsageRecords API") + @Parameter(name= ApiConstants.HIDE_IP_ADDRESS_USAGE, type=CommandType.BOOLEAN, description = "When true IP address usage for the Network will not be exported by the listUsageRecords API") private Boolean hideIpAddressUsage; public Boolean getHideIpAddressUsage() { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateNetworkOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateNetworkOfferingCmd.java index 75fb45e1f115..df9f6ad0664d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateNetworkOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateNetworkOfferingCmd.java @@ -16,7 +16,6 @@ // under the License. package org.apache.cloudstack.api.command.admin.network; -import java.util.ArrayList; import java.util.List; import org.apache.cloudstack.api.APICommand; @@ -26,64 +25,63 @@ import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.offering.DomainAndZoneIdResolver; import org.apache.cloudstack.api.response.NetworkOfferingResponse; -import org.apache.commons.lang3.StringUtils; -import com.cloud.dc.DataCenter; -import com.cloud.domain.Domain; -import com.cloud.exception.InvalidParameterValueException; + import com.cloud.offering.NetworkOffering; import com.cloud.user.Account; @APICommand(name = "updateNetworkOffering", description = "Updates a network offering.", responseObject = NetworkOfferingResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class UpdateNetworkOfferingCmd extends BaseCmd { +public class UpdateNetworkOfferingCmd extends BaseCmd implements DomainAndZoneIdResolver { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = NetworkOfferingResponse.class, description = "the id of the network offering") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = NetworkOfferingResponse.class, description = "The ID of the network offering") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the network offering") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the network offering") private String networkOfferingName; - @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "the display text of the network offering") + @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "The display text of the network offering") private String displayText; - @Parameter(name = ApiConstants.AVAILABILITY, type = CommandType.STRING, description = "the availability of network offering." + @Parameter(name = ApiConstants.AVAILABILITY, type = CommandType.STRING, description = "The availability of network offering." + " The value is Required makes this network offering default for Guest Virtual Networks. Only one network offering can have the value Required ") private String availability; - @Parameter(name = ApiConstants.SORT_KEY, type = CommandType.INTEGER, description = "sort key of the network offering, integer") + @Parameter(name = ApiConstants.SORT_KEY, type = CommandType.INTEGER, description = "Sort key of the network offering, integer") private Integer sortKey; - @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "update state for the network offering") + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "Update state for the network offering") private String state; @Parameter(name = ApiConstants.KEEPALIVE_ENABLED, type = CommandType.BOOLEAN, required = false, - description = "if true keepalive will be turned on in the loadbalancer. At the time of writing this has only an effect on haproxy; the mode http and httpclose options are unset in the haproxy conf file.") + description = "If true keepalive will be turned on in the loadbalancer. At the time of writing this has only an effect on haproxy; the mode http and httpclose options are unset in the haproxy conf file.") private Boolean keepAliveEnabled; @Parameter(name = ApiConstants.MAX_CONNECTIONS, type = CommandType.INTEGER, - description = "maximum number of concurrent connections supported by the network offering") + description = "Maximum number of concurrent connections supported by the network offering") private Integer maxConnections; - @Parameter(name = ApiConstants.TAGS, type = CommandType.STRING, description = "the tags for the network offering.", length = 4096) + @Parameter(name = ApiConstants.TAGS, type = CommandType.STRING, description = "The tags for the network offering.", length = 4096) private String tags; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.STRING, - description = "the ID of the containing domain(s) as comma separated string, public for public offerings") + length = 4096, + description = "The ID of the containing domain(s) as comma separated string, public for public offerings") private String domainIds; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.STRING, - description = "the ID of the containing zone(s) as comma separated string, all for all zones offerings", + description = "The ID of the containing zone(s) as comma separated string, all for all zones offerings", since = "4.13", length = 4096) private String zoneIds; @@ -129,63 +127,11 @@ public String getTags() { } public List getDomainIds() { - List validDomainIds = new ArrayList<>(); - if (StringUtils.isNotEmpty(domainIds)) { - if (domainIds.contains(",")) { - String[] domains = domainIds.split(","); - for (String domain : domains) { - Domain validDomain = _entityMgr.findByUuid(Domain.class, domain.trim()); - if (validDomain != null) { - validDomainIds.add(validDomain.getId()); - } else { - throw new InvalidParameterValueException("Failed to create network offering because invalid domain has been specified."); - } - } - } else { - domainIds = domainIds.trim(); - if (!domainIds.matches("public")) { - Domain validDomain = _entityMgr.findByUuid(Domain.class, domainIds.trim()); - if (validDomain != null) { - validDomainIds.add(validDomain.getId()); - } else { - throw new InvalidParameterValueException("Failed to create network offering because invalid domain has been specified."); - } - } - } - } else { - validDomainIds.addAll(_configService.getNetworkOfferingDomains(id)); - } - return validDomainIds; + return resolveDomainIds(domainIds, id, _configService::getNetworkOfferingDomains, "network offering"); } public List getZoneIds() { - List validZoneIds = new ArrayList<>(); - if (StringUtils.isNotEmpty(zoneIds)) { - if (zoneIds.contains(",")) { - String[] zones = zoneIds.split(","); - for (String zone : zones) { - DataCenter validZone = _entityMgr.findByUuid(DataCenter.class, zone.trim()); - if (validZone != null) { - validZoneIds.add(validZone.getId()); - } else { - throw new InvalidParameterValueException("Failed to create network offering because invalid zone has been specified."); - } - } - } else { - zoneIds = zoneIds.trim(); - if (!zoneIds.matches("all")) { - DataCenter validZone = _entityMgr.findByUuid(DataCenter.class, zoneIds.trim()); - if (validZone != null) { - validZoneIds.add(validZone.getId()); - } else { - throw new InvalidParameterValueException("Failed to create network offering because invalid zone has been specified."); - } - } - } - } else { - validZoneIds.addAll(_configService.getNetworkOfferingZones(id)); - } - return validZoneIds; + return resolveZoneIds(zoneIds, id, _configService::getNetworkOfferingZones, "network offering"); } ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateNetworkServiceProviderCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateNetworkServiceProviderCmd.java index b4801d9368eb..e0ce0aade1ee 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateNetworkServiceProviderCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateNetworkServiceProviderCmd.java @@ -47,13 +47,13 @@ public class UpdateNetworkServiceProviderCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "Enabled/Disabled/Shutdown the physical network service provider") private String state; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ProviderResponse.class, required = true, description = "network service provider id") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ProviderResponse.class, required = true, description = "Network service provider ID") private Long id; @Parameter(name = ApiConstants.SERVICE_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, - description = "the list of services to be enabled for this physical network service provider") + description = "The list of services to be enabled for this physical network service provider") private List enabledServices; ///////////////////////////////////////////////////// @@ -100,7 +100,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Updating physical network ServiceProvider: " + getId(); + return "Updating Physical Network ServiceProvider with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdatePhysicalNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdatePhysicalNetworkCmd.java index 162116470bd5..6a6264e418ce 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdatePhysicalNetworkCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdatePhysicalNetworkCmd.java @@ -38,19 +38,19 @@ public class UpdatePhysicalNetworkCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, required = true, description = "physical network id") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, required = true, description = "Physical Network ID") private Long id; - @Parameter(name = ApiConstants.NETWORK_SPEED, type = CommandType.STRING, description = "the speed for the physical network[1G/10G]") + @Parameter(name = ApiConstants.NETWORK_SPEED, type = CommandType.STRING, description = "The speed for the physical Network[1G/10G]") private String speed; - @Parameter(name = ApiConstants.TAGS, type = CommandType.LIST, collectionType = CommandType.STRING, description = "Tag the physical network") + @Parameter(name = ApiConstants.TAGS, type = CommandType.LIST, collectionType = CommandType.STRING, description = "Tag the physical Network") private List tags; @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "Enabled/Disabled") private String state; - @Parameter(name = ApiConstants.VLAN, type = CommandType.STRING, description = "the VLAN for the physical network") + @Parameter(name = ApiConstants.VLAN, type = CommandType.STRING, description = "The VLAN for the physical Network") private String vlan; ///////////////////////////////////////////////////// @@ -98,7 +98,7 @@ public void execute() { @Override public String getEventDescription() { - return "Updating Physical network: " + getId(); + return "Updating Physical Network with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdatePodManagementNetworkIpRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdatePodManagementNetworkIpRangeCmd.java index 6f90a070f0d1..0dfa83a68289 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdatePodManagementNetworkIpRangeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdatePodManagementNetworkIpRangeCmd.java @@ -113,7 +113,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Updating pod management IP range " + getNewStartIP() + "-" + getNewEndIP() + " of Pod: " + getPodId(); + return "Updating pod management IP range " + getNewStartIP() + "-" + getNewEndIP() + " of Pod: " + getResourceUuid(ApiConstants.POD_ID); } @Override @@ -139,7 +139,7 @@ public void execute() { logger.warn("Exception: ", ex); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); } catch (Exception e) { - logger.warn("Failed to update pod management IP range " + getNewStartIP() + "-" + getNewEndIP() + " of Pod: " + getPodId(), e); + logger.warn("Failed to update pod management IP range {}-{} of Pod: {}", getNewStartIP(), getNewEndIP(), getPodId(), e); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateStorageNetworkIpRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateStorageNetworkIpRangeCmd.java index 65e2437417de..978e94a783a9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateStorageNetworkIpRangeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateStorageNetworkIpRangeCmd.java @@ -51,16 +51,16 @@ public class UpdateStorageNetworkIpRangeCmd extends BaseAsyncCmd { description = "UUID of storage network ip range") private Long id; - @Parameter(name = ApiConstants.START_IP, type = CommandType.STRING, description = "the beginning IP address") + @Parameter(name = ApiConstants.START_IP, type = CommandType.STRING, description = "The beginning IP address") private String startIp; - @Parameter(name = ApiConstants.END_IP, type = CommandType.STRING, description = "the ending IP address") + @Parameter(name = ApiConstants.END_IP, type = CommandType.STRING, description = "The ending IP address") private String endIp; @Parameter(name = ApiConstants.VLAN, type = CommandType.INTEGER, description = "Optional. the vlan the ip range sits on") private Integer vlan; - @Parameter(name = ApiConstants.NETMASK, type = CommandType.STRING, description = "the netmask for storage network") + @Parameter(name = ApiConstants.NETMASK, type = CommandType.STRING, description = "The netmask for storage network") private String netmask; ///////////////////////////////////////////////////// @@ -93,7 +93,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Update storage ip range " + getId() + " [StartIp=" + getStartIp() + ", EndIp=" + getEndIp() + ", vlan=" + getVlan() + ", netmask=" + getNetmask() + ']'; + return "Updating storage IP range " + getResourceUuid(ApiConstants.ID) + " [StartIp=" + getStartIp() + ", EndIp=" + getEndIp() + ", VLAN=" + getVlan() + ", netmask=" + getNetmask() + ']'; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForNetworkCmd.java index 1d6bffca342e..3c58cbb3c532 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForNetworkCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForNetworkCmd.java @@ -80,7 +80,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Changing Bgp Peers for network " + getNetworkId(); + return "Changing BGP Peers for Network with ID: " + getResourceUuid(ApiConstants.NETWORK_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForVpcCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForVpcCmd.java index 0c89f3f1d43c..8784f0672790 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForVpcCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForVpcCmd.java @@ -80,7 +80,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Changing Bgp Peers for VPC " + getVpcId(); + return "Changing BGP Peers for VPC with ID: " + getResourceUuid(ApiConstants.VPC_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/CreateBgpPeerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/CreateBgpPeerCmd.java index 80642124938a..f1d9b6723091 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/CreateBgpPeerCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/CreateBgpPeerCmd.java @@ -145,7 +145,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Creating Bgp Peer " + getAsNumber() + " for zone=" + getZoneId(); + return "Creating BGP Peer " + getAsNumber() + " for zone with ID: " + getResourceUuid(ApiConstants.ZONE_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/DedicateBgpPeerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/DedicateBgpPeerCmd.java index ec3d0ea11629..f1ef963e9872 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/DedicateBgpPeerCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/DedicateBgpPeerCmd.java @@ -82,7 +82,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Dedicating Bgp Peer " + getId(); + return "Dedicating BGP Peer with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/DeleteBgpPeerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/DeleteBgpPeerCmd.java index a01711efa44f..a412e91bc48e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/DeleteBgpPeerCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/DeleteBgpPeerCmd.java @@ -59,7 +59,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Deleting Bgp Peer " + getId(); + return "Deleting BGP Peer with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ReleaseDedicatedBgpPeerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ReleaseDedicatedBgpPeerCmd.java index 92610c233ef0..c754d443c051 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ReleaseDedicatedBgpPeerCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ReleaseDedicatedBgpPeerCmd.java @@ -59,7 +59,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Releasing a dedicated Bgp Peer " + getId(); + return "Releasing dedicated BGP Peer with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/UpdateBgpPeerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/UpdateBgpPeerCmd.java index ae44330ea033..f45c1ee5a2f3 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/UpdateBgpPeerCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/UpdateBgpPeerCmd.java @@ -120,7 +120,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Updating Bgp Peer " + getId(); + return "Updating BGP Peer with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CloneDiskOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CloneDiskOfferingCmd.java new file mode 100644 index 000000000000..8d822be203ad --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CloneDiskOfferingCmd.java @@ -0,0 +1,73 @@ +// 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.offering; + +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.DiskOfferingResponse; + +import com.cloud.offering.DiskOffering; + +@APICommand(name = "cloneDiskOffering", + description = "Clones a disk offering. All parameters from createDiskOffering are available. If not specified, values will be copied from the source offering.", + responseObject = DiskOfferingResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + since = "4.23.0", + authorized = {RoleType.Admin, RoleType.DomainAdmin}) +public class CloneDiskOfferingCmd extends CreateDiskOfferingCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.SOURCE_OFFERING_ID, + type = BaseCmd.CommandType.UUID, + entityType = DiskOfferingResponse.class, + required = true, + description = "The ID of the source disk offering to clone from") + private Long sourceOfferingId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getSourceOfferingId() { + return sourceOfferingId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + DiskOffering result = _configService.cloneDiskOffering(this); + if (result != null) { + DiskOfferingResponse response = _responseGenerator.createDiskOfferingResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to clone disk offering"); + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CloneServiceOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CloneServiceOfferingCmd.java new file mode 100644 index 000000000000..d01ca4c195da --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CloneServiceOfferingCmd.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.offering; + +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.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ServiceOfferingResponse; + +import com.cloud.offering.ServiceOffering; + +@APICommand(name = "cloneServiceOffering", + description = "Clones a service offering. All parameters from createServiceOffering are available. If not specified, values will be copied from the source offering.", + responseObject = ServiceOfferingResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + since = "4.23.0", + authorized = {RoleType.Admin, RoleType.DomainAdmin}) +public class CloneServiceOfferingCmd extends CreateServiceOfferingCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.SOURCE_OFFERING_ID, + type = CommandType.UUID, + entityType = ServiceOfferingResponse.class, + required = true, + description = "The ID of the source service offering to clone from") + private Long sourceOfferingId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getSourceOfferingId() { + return sourceOfferingId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + + @Override + public void execute() { + try { + ServiceOffering result = _configService.cloneServiceOffering(this); + if (result != null) { + ServiceOfferingResponse response = _responseGenerator.createServiceOfferingResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to clone service offering"); + } + } catch (com.cloud.exception.InvalidParameterValueException e) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, e.getMessage()); + } catch (com.cloud.utils.exception.CloudRuntimeException e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java index c46e4cd6b445..e1ff90e4bd89 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java @@ -52,106 +52,105 @@ public class CreateDiskOfferingCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.DISK_SIZE, type = CommandType.LONG, required = false, description = "size of the disk offering in GB (1GB = 1,073,741,824 bytes)") + @Parameter(name = ApiConstants.DISK_SIZE, type = CommandType.LONG, required = false, description = "Size of the disk offering in GB (1GB = 1,073,741,824 bytes)") private Long diskSize; @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "An alternate display text of the disk offering, defaults to 'name'.", length = 4096) private String displayText; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "name of the disk offering") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the disk offering") private String offeringName; - @Parameter(name = ApiConstants.TAGS, type = CommandType.STRING, description = "tags for the disk offering", length = 4096) + @Parameter(name = ApiConstants.TAGS, type = CommandType.STRING, description = "Tags for the disk offering", length = 4096) private String tags; - @Parameter(name = ApiConstants.CUSTOMIZED, type = CommandType.BOOLEAN, description = "whether disk offering size is custom or not") + @Parameter(name = ApiConstants.CUSTOMIZED, type = CommandType.BOOLEAN, description = "Whether disk offering size is custom or not") private Boolean customized; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = DomainResponse.class, - description = "the ID of the containing domain(s), null for public offerings") + description = "The ID of the containing domain(s), null for public offerings") private List domainIds; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = ZoneResponse.class, - description = "the ID of the containing zone(s), null for public offerings", + description = "The ID of the containing zone(s), null for public offerings", since = "4.13") private List zoneIds; - @Parameter(name = ApiConstants.STORAGE_TYPE, type = CommandType.STRING, description = "the storage type of the disk offering. Values are local and shared.") + @Parameter(name = ApiConstants.STORAGE_TYPE, type = CommandType.STRING, description = "The storage type of the disk offering. Values are local and shared.") private String storageType = ServiceOffering.StorageType.shared.toString(); @Parameter(name = ApiConstants.PROVISIONINGTYPE, type = CommandType.STRING, - description = "provisioning type used to create volumes. Valid values are thin, sparse, fat.") + description = "Provisioning type used to create volumes. Valid values are thin, sparse, fat.") private String provisioningType = ProvisioningType.THIN.toString(); @Parameter(name = ApiConstants.DISPLAY_OFFERING, type = CommandType.BOOLEAN, - description = "an optional field, whether to display the offering to the end user or not.") + description = "An optional field, whether to display the offering to the end user or not.") private Boolean displayOffering; - @Parameter(name = ApiConstants.BYTES_READ_RATE, type = CommandType.LONG, required = false, description = "bytes read rate of the disk offering") + @Parameter(name = ApiConstants.BYTES_READ_RATE, type = CommandType.LONG, required = false, description = "Bytes read rate of the disk offering") private Long bytesReadRate; - @Parameter(name = ApiConstants.BYTES_READ_RATE_MAX, type = CommandType.LONG, required = false, description = "burst bytes read rate of the disk offering") + @Parameter(name = ApiConstants.BYTES_READ_RATE_MAX, type = CommandType.LONG, required = false, description = "Burst bytes read rate of the disk offering") private Long bytesReadRateMax; - @Parameter(name = ApiConstants.BYTES_READ_RATE_MAX_LENGTH, type = CommandType.LONG, required = false, description = "length (in seconds) of the burst") + @Parameter(name = ApiConstants.BYTES_READ_RATE_MAX_LENGTH, type = CommandType.LONG, required = false, description = "Length (in seconds) of the burst") private Long bytesReadRateMaxLength; - @Parameter(name = ApiConstants.BYTES_WRITE_RATE, type = CommandType.LONG, required = false, description = "bytes write rate of the disk offering") + @Parameter(name = ApiConstants.BYTES_WRITE_RATE, type = CommandType.LONG, required = false, description = "Bytes write rate of the disk offering") private Long bytesWriteRate; - @Parameter(name = ApiConstants.BYTES_WRITE_RATE_MAX, type = CommandType.LONG, required = false, description = "burst bytes write rate of the disk offering") + @Parameter(name = ApiConstants.BYTES_WRITE_RATE_MAX, type = CommandType.LONG, required = false, description = "Burst bytes write rate of the disk offering") private Long bytesWriteRateMax; - @Parameter(name = ApiConstants.BYTES_WRITE_RATE_MAX_LENGTH, type = CommandType.LONG, required = false, description = "length (in seconds) of the burst") + @Parameter(name = ApiConstants.BYTES_WRITE_RATE_MAX_LENGTH, type = CommandType.LONG, required = false, description = "Length (in seconds) of the burst") private Long bytesWriteRateMaxLength; - @Parameter(name = ApiConstants.IOPS_READ_RATE, type = CommandType.LONG, required = false, description = "io requests read rate of the disk offering") + @Parameter(name = ApiConstants.IOPS_READ_RATE, type = CommandType.LONG, required = false, description = "I/O requests read rate of the disk offering") private Long iopsReadRate; - @Parameter(name = ApiConstants.IOPS_READ_RATE_MAX, type = CommandType.LONG, required = false, description = "burst requests read rate of the disk offering") + @Parameter(name = ApiConstants.IOPS_READ_RATE_MAX, type = CommandType.LONG, required = false, description = "Burst requests read rate of the disk offering") private Long iopsReadRateMax; - @Parameter(name = ApiConstants.IOPS_READ_RATE_MAX_LENGTH, type = CommandType.LONG, required = false, description = "length (in seconds) of the burst") + @Parameter(name = ApiConstants.IOPS_READ_RATE_MAX_LENGTH, type = CommandType.LONG, required = false, description = "Length (in seconds) of the burst") private Long iopsReadRateMaxLength; - @Parameter(name = ApiConstants.IOPS_WRITE_RATE, type = CommandType.LONG, required = false, description = "io requests write rate of the disk offering") + @Parameter(name = ApiConstants.IOPS_WRITE_RATE, type = CommandType.LONG, required = false, description = "I/O requests write rate of the disk offering") private Long iopsWriteRate; - @Parameter(name = ApiConstants.IOPS_WRITE_RATE_MAX, type = CommandType.LONG, required = false, description = "burst io requests write rate of the disk offering") + @Parameter(name = ApiConstants.IOPS_WRITE_RATE_MAX, type = CommandType.LONG, required = false, description = "Burst I/O requests write rate of the disk offering") private Long iopsWriteRateMax; - @Parameter(name = ApiConstants.IOPS_WRITE_RATE_MAX_LENGTH, type = CommandType.LONG, required = false, description = "length (in seconds) of the burst") + @Parameter(name = ApiConstants.IOPS_WRITE_RATE_MAX_LENGTH, type = CommandType.LONG, required = false, description = "Length (in seconds) of the burst") private Long iopsWriteRateMaxLength; - @Parameter(name = ApiConstants.CUSTOMIZED_IOPS, type = CommandType.BOOLEAN, required = false, description = "whether disk offering iops is custom or not") + @Parameter(name = ApiConstants.CUSTOMIZED_IOPS, type = CommandType.BOOLEAN, required = false, description = "Whether disk offering IOPS is custom or not") private Boolean customizedIops; - @Parameter(name = ApiConstants.MIN_IOPS, type = CommandType.LONG, required = false, description = "min iops of the disk offering") + @Parameter(name = ApiConstants.MIN_IOPS, type = CommandType.LONG, required = false, description = "Min IOPS of the disk offering") private Long minIops; - @Parameter(name = ApiConstants.MAX_IOPS, type = CommandType.LONG, required = false, description = "max iops of the disk offering") + @Parameter(name = ApiConstants.MAX_IOPS, type = CommandType.LONG, required = false, description = "Max IOPS of the disk offering") private Long maxIops; @Parameter(name = ApiConstants.HYPERVISOR_SNAPSHOT_RESERVE, type = CommandType.INTEGER, required = false, - description = "Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)") + description = "Hypervisor Snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)") private Integer hypervisorSnapshotReserve; @Parameter(name = ApiConstants.CACHE_MODE, type = CommandType.STRING, - required = false, - description = "the cache mode to use for this disk offering. none, writeback or writethrough", + description = "The cache mode to use for this disk offering. none, writeback, writethrough or hypervisor default. If the hypervisor default cache mode is used on other hypervisors than KVM, it will fall back to none cache mode", since = "4.14") private String cacheMode; @@ -164,7 +163,7 @@ public class CreateDiskOfferingCmd extends BaseCmd { @Parameter(name = ApiConstants.ENCRYPT, type = CommandType.BOOLEAN, required=false, description = "Volumes using this offering should be encrypted", since = "4.18") private Boolean encrypt; - @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "details to specify disk offering parameters", since = "4.16") + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "Details to specify disk offering parameters", since = "4.16") private Map details; 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 3d20ed50a5db..4363d6861ba1 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 @@ -54,143 +54,142 @@ public class CreateServiceOfferingCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.CPU_NUMBER, type = CommandType.INTEGER, required = false, description = "the CPU number of the service offering") + @Parameter(name = ApiConstants.CPU_NUMBER, type = CommandType.INTEGER, required = false, description = "The CPU number of the service offering") private Integer cpuNumber; @Parameter(name = ApiConstants.CPU_SPEED, type = CommandType.INTEGER, required = false, description = "For VMware and Xen based hypervisors this is the CPU speed of the service offering in MHz.\n" + "For the KVM hypervisor," + " the values of the parameters cpuSpeed and cpuNumber will be used to calculate the `shares` value. This value is used by the KVM hypervisor to calculate how much time" + - " the VM will have access to the host's CPU. The `shares` value does not have a unit, and its purpose is being a weight value for the host to compare between its guest" + - " VMs. For more information, see https://libvirt.org/formatdomain.html#cpu-tuning.") + " the Instance will have access to the host's CPU. The `shares` value does not have a unit, and its purpose is being a weight value for the host to compare between its guest" + + " Instances. For more information, see https://libvirt.org/formatdomain.html#cpu-tuning.") private Integer cpuSpeed; @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "The display text of the service offering, defaults to 'name'.") private String displayText; - @Parameter(name = ApiConstants.PROVISIONINGTYPE, type = CommandType.STRING, description = "provisioning type used to create volumes. Valid values are thin, sparse, fat.") + @Parameter(name = ApiConstants.PROVISIONINGTYPE, type = CommandType.STRING, description = "Provisioning type used to create volumes. Valid values are thin, sparse, fat.") private String provisioningType = Storage.ProvisioningType.THIN.toString(); - @Parameter(name = ApiConstants.MEMORY, type = CommandType.INTEGER, required = false, description = "the total memory of the service offering in MB") + @Parameter(name = ApiConstants.MEMORY, type = CommandType.INTEGER, required = false, description = "The total memory of the service offering in MB") private Integer memory; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the service offering") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "The name of the service offering") private String serviceOfferingName; - @Parameter(name = ApiConstants.OFFER_HA, type = CommandType.BOOLEAN, description = "the HA for the service offering") + @Parameter(name = ApiConstants.OFFER_HA, type = CommandType.BOOLEAN, description = "The HA for the service offering") private Boolean offerHa; - @Parameter(name = ApiConstants.LIMIT_CPU_USE, type = CommandType.BOOLEAN, description = "restrict the CPU usage to committed service offering") + @Parameter(name = ApiConstants.LIMIT_CPU_USE, type = CommandType.BOOLEAN, description = "Restrict the CPU usage to committed service offering") private Boolean limitCpuUse; @Parameter(name = ApiConstants.IS_VOLATILE, type = CommandType.BOOLEAN, - description = "true if the virtual machine needs to be volatile so that on every reboot of VM, original root disk is dettached then destroyed and a fresh root disk is created and attached to VM") + description = "True if the Instance needs to be volatile so that on every reboot, the original root disk is detached, then destroyed and a fresh root disk is created and attached to the Instance") private Boolean isVolatile; - @Parameter(name = ApiConstants.STORAGE_TYPE, type = CommandType.STRING, description = "the storage type of the service offering. Values are local and shared.") + @Parameter(name = ApiConstants.STORAGE_TYPE, type = CommandType.STRING, description = "The storage type of the service offering. Values are local and shared.") private String storageType; - @Parameter(name = ApiConstants.TAGS, type = CommandType.STRING, description = "the tags for this service offering.") + @Parameter(name = ApiConstants.TAGS, type = CommandType.STRING, description = "The tags for this service offering.") private String tags; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = DomainResponse.class, - description = "the ID of the containing domain(s), null for public offerings") + description = "The ID of the containing domain(s), null for public offerings") private List domainIds; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = ZoneResponse.class, - description = "the ID of the containing zone(s), null for public offerings", + description = "The ID of the containing zone(s), null for public offerings", since = "4.13") private List zoneIds; - @Parameter(name = ApiConstants.HOST_TAGS, type = CommandType.STRING, description = "the host tag for this service offering.") + @Parameter(name = ApiConstants.HOST_TAGS, type = CommandType.STRING, description = "The host tag for this service offering.") private String hostTag; - @Parameter(name = ApiConstants.IS_SYSTEM_OFFERING, type = CommandType.BOOLEAN, description = "is this a system vm offering") + @Parameter(name = ApiConstants.IS_SYSTEM_OFFERING, type = CommandType.BOOLEAN, description = "Is this a system vm offering") private Boolean isSystem; @Parameter(name = ApiConstants.SYSTEM_VM_TYPE, type = CommandType.STRING, - description = "the system VM type. Possible types are \"domainrouter\", \"consoleproxy\" and \"secondarystoragevm\".") + description = "The system VM type. Possible types are \"domainrouter\", \"consoleproxy\" and \"secondarystoragevm\".") private String systemVmType; @Parameter(name = ApiConstants.NETWORKRATE, type = CommandType.INTEGER, - description = "data transfer rate in megabits per second allowed. Supported only for non-System offering and system offerings having \"domainrouter\" systemvmtype") + description = "Data transfer rate in megabits per second allowed. Supported only for non-System offering and system offerings having \"domainrouter\" systemvmtype") private Integer networkRate; @Parameter(name = ApiConstants.DEPLOYMENT_PLANNER, type = CommandType.STRING, - description = "The deployment planner heuristics used to deploy a VM of this offering. If null, value of global config vm.deployment.planner is used") + description = "The deployment planner heuristics used to deploy an Instance of this offering. If null, value of global config vm.deployment.planner is used") private String deploymentPlanner; - @Parameter(name = ApiConstants.SERVICE_OFFERING_DETAILS, type = CommandType.MAP, description = "details for planner, used to store specific parameters") + @Parameter(name = ApiConstants.SERVICE_OFFERING_DETAILS, type = CommandType.MAP, description = "Details for planner, used to store specific parameters") private Map details; - @Parameter(name = ApiConstants.ROOT_DISK_SIZE, type = CommandType.LONG, since = "4.15", description = "the Root disk size in GB.") + @Parameter(name = ApiConstants.ROOT_DISK_SIZE, type = CommandType.LONG, since = "4.15", description = "The Root disk size in GB.") private Long rootDiskSize; - @Parameter(name = ApiConstants.BYTES_READ_RATE, type = CommandType.LONG, required = false, description = "bytes read rate of the disk offering") + @Parameter(name = ApiConstants.BYTES_READ_RATE, type = CommandType.LONG, required = false, description = "Bytes read rate of the disk offering") private Long bytesReadRate; - @Parameter(name = ApiConstants.BYTES_READ_RATE_MAX, type = CommandType.LONG, required = false, description = "burst bytes read rate of the disk offering") + @Parameter(name = ApiConstants.BYTES_READ_RATE_MAX, type = CommandType.LONG, required = false, description = "Burst bytes read rate of the disk offering") private Long bytesReadRateMax; - @Parameter(name = ApiConstants.BYTES_READ_RATE_MAX_LENGTH, type = CommandType.LONG, required = false, description = "length (in seconds) of the burst") + @Parameter(name = ApiConstants.BYTES_READ_RATE_MAX_LENGTH, type = CommandType.LONG, required = false, description = "Length (in seconds) of the burst") private Long bytesReadRateMaxLength; - @Parameter(name = ApiConstants.BYTES_WRITE_RATE, type = CommandType.LONG, required = false, description = "bytes write rate of the disk offering") + @Parameter(name = ApiConstants.BYTES_WRITE_RATE, type = CommandType.LONG, required = false, description = "Bytes write rate of the disk offering") private Long bytesWriteRate; - @Parameter(name = ApiConstants.BYTES_WRITE_RATE_MAX, type = CommandType.LONG, required = false, description = "burst bytes write rate of the disk offering") + @Parameter(name = ApiConstants.BYTES_WRITE_RATE_MAX, type = CommandType.LONG, required = false, description = "Burst bytes write rate of the disk offering") private Long bytesWriteRateMax; - @Parameter(name = ApiConstants.BYTES_WRITE_RATE_MAX_LENGTH, type = CommandType.LONG, required = false, description = "length (in seconds) of the burst") + @Parameter(name = ApiConstants.BYTES_WRITE_RATE_MAX_LENGTH, type = CommandType.LONG, required = false, description = "Length (in seconds) of the burst") private Long bytesWriteRateMaxLength; - @Parameter(name = ApiConstants.IOPS_READ_RATE, type = CommandType.LONG, required = false, description = "io requests read rate of the disk offering") + @Parameter(name = ApiConstants.IOPS_READ_RATE, type = CommandType.LONG, required = false, description = "I/O requests read rate of the disk offering") private Long iopsReadRate; - @Parameter(name = ApiConstants.IOPS_READ_RATE_MAX, type = CommandType.LONG, required = false, description = "burst requests read rate of the disk offering") + @Parameter(name = ApiConstants.IOPS_READ_RATE_MAX, type = CommandType.LONG, required = false, description = "Burst requests read rate of the disk offering") private Long iopsReadRateMax; - @Parameter(name = ApiConstants.IOPS_READ_RATE_MAX_LENGTH, type = CommandType.LONG, required = false, description = "length (in seconds) of the burst") + @Parameter(name = ApiConstants.IOPS_READ_RATE_MAX_LENGTH, type = CommandType.LONG, required = false, description = "Length (in seconds) of the burst") private Long iopsReadRateMaxLength; - @Parameter(name = ApiConstants.IOPS_WRITE_RATE, type = CommandType.LONG, required = false, description = "io requests write rate of the disk offering") + @Parameter(name = ApiConstants.IOPS_WRITE_RATE, type = CommandType.LONG, required = false, description = "I/O requests write rate of the disk offering") private Long iopsWriteRate; - @Parameter(name = ApiConstants.IOPS_WRITE_RATE_MAX, type = CommandType.LONG, required = false, description = "burst io requests write rate of the disk offering") + @Parameter(name = ApiConstants.IOPS_WRITE_RATE_MAX, type = CommandType.LONG, required = false, description = "Burst io requests write rate of the disk offering") private Long iopsWriteRateMax; - @Parameter(name = ApiConstants.IOPS_WRITE_RATE_MAX_LENGTH, type = CommandType.LONG, required = false, description = "length (in seconds) of the burst") + @Parameter(name = ApiConstants.IOPS_WRITE_RATE_MAX_LENGTH, type = CommandType.LONG, required = false, description = "Length (in seconds) of the burst") private Long iopsWriteRateMaxLength; - @Parameter(name = ApiConstants.CUSTOMIZED_IOPS, type = CommandType.BOOLEAN, required = false, description = "whether compute offering iops is custom or not", since = "4.4") + @Parameter(name = ApiConstants.CUSTOMIZED_IOPS, type = CommandType.BOOLEAN, required = false, description = "Whether compute offering iops is custom or not", since = "4.4") private Boolean customizedIops; - @Parameter(name = ApiConstants.MIN_IOPS, type = CommandType.LONG, required = false, description = "min iops of the compute offering", since = "4.4") + @Parameter(name = ApiConstants.MIN_IOPS, type = CommandType.LONG, required = false, description = "Min iops of the compute offering", since = "4.4") private Long minIops; - @Parameter(name = ApiConstants.MAX_IOPS, type = CommandType.LONG, required = false, description = "max iops of the compute offering", since = "4.4") + @Parameter(name = ApiConstants.MAX_IOPS, type = CommandType.LONG, required = false, description = "Max iops of the compute offering", since = "4.4") private Long maxIops; @Parameter(name = ApiConstants.HYPERVISOR_SNAPSHOT_RESERVE, type = CommandType.INTEGER, required = false, - description = "Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)", + description = "Hypervisor Snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)", since = "4.4") private Integer hypervisorSnapshotReserve; @Parameter(name = ApiConstants.CACHE_MODE, type = CommandType.STRING, - required = false, - description = "the cache mode to use for this disk offering. none, writeback or writethrough", + description = "The cache mode to use for this disk offering. none, writeback, writethrough or hypervisor default. If the hypervisor default cache mode is used on other hypervisors than KVM, it will fall back to none cache mode", since = "4.14") private String cacheMode; @@ -229,14 +228,14 @@ public class CreateServiceOfferingCmd extends BaseCmd { private Long storagePolicy; @Parameter(name = ApiConstants.DYNAMIC_SCALING_ENABLED, type = CommandType.BOOLEAN, since = "4.16", - description = "true if virtual machine needs to be dynamically scalable of cpu or memory") + description = "True if Instance needs to be dynamically scalable of cpu or memory") protected Boolean isDynamicScalingEnabled; @Parameter(name = ApiConstants.DISK_OFFERING_ID, required = false, type = CommandType.UUID, entityType = DiskOfferingResponse.class, - description = "the ID of the disk offering to which service offering should be mapped", + description = "The ID of the disk offering to which service offering should be mapped", since = "4.17") private Long diskOfferingId; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/DeleteServiceOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/DeleteServiceOfferingCmd.java index 19203289d104..d08b4f144c26 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/DeleteServiceOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/DeleteServiceOfferingCmd.java @@ -40,7 +40,7 @@ public class DeleteServiceOfferingCmd extends BaseCmd { type = CommandType.UUID, entityType = ServiceOfferingResponse.class, required = true, - description = "the ID of the service offering") + description = "The ID of the service offering") private Long id; ///////////////////////////////////////////////////// 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 fcd6b03d3e59..4b1cd2ff7255 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 @@ -26,7 +26,8 @@ import org.apache.cloudstack.api.response.IsAccountAllowedToCreateOfferingsWithTagsResponse; @APICommand(name = "isAccountAllowedToCreateOfferingsWithTags", description = "Return true if the specified account is allowed to create offerings with tags.", - responseObject = IsAccountAllowedToCreateOfferingsWithTagsResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) + responseObject = IsAccountAllowedToCreateOfferingsWithTagsResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + httpMethod = "GET") public class IsAccountAllowedToCreateOfferingsWithTagsCmd extends BaseCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "Account UUID", required = true) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateDiskOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateDiskOfferingCmd.java index 370453804cfc..4d48327eeb77 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateDiskOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateDiskOfferingCmd.java @@ -16,7 +16,6 @@ // under the License. package org.apache.cloudstack.api.command.admin.offering; -import java.util.ArrayList; import java.util.List; import com.cloud.offering.DiskOffering.State; @@ -27,19 +26,18 @@ import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.offering.DomainAndZoneIdResolver; import org.apache.cloudstack.api.response.DiskOfferingResponse; import org.apache.commons.lang3.EnumUtils; import org.apache.commons.lang3.StringUtils; -import com.cloud.dc.DataCenter; -import com.cloud.domain.Domain; import com.cloud.exception.InvalidParameterValueException; import com.cloud.offering.DiskOffering; import com.cloud.user.Account; @APICommand(name = "updateDiskOffering", description = "Updates a disk offering.", responseObject = DiskOfferingResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class UpdateDiskOfferingCmd extends BaseCmd { +public class UpdateDiskOfferingCmd extends BaseCmd implements DomainAndZoneIdResolver { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// @@ -47,80 +45,81 @@ public class UpdateDiskOfferingCmd extends BaseCmd { @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, - description = "updates alternate display text of the disk offering with this value", + description = "Updates alternate display text of the disk offering with this value", length = 4096) private String displayText; @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DiskOfferingResponse.class, required = true, description = "ID of the disk offering") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "updates name of the disk offering with this value") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Updates name of the disk offering with this value") private String diskOfferingName; - @Parameter(name = ApiConstants.SORT_KEY, type = CommandType.INTEGER, description = "sort key of the disk offering, integer") + @Parameter(name = ApiConstants.SORT_KEY, type = CommandType.INTEGER, description = "Sort key of the disk offering, integer") private Integer sortKey; @Parameter(name = ApiConstants.DISPLAY_OFFERING, type = CommandType.BOOLEAN, - description = "an optional field, whether to display the offering to the end user or not.") + description = "An optional field, whether to display the offering to the end user or not.") private Boolean displayOffering; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.STRING, - description = "the ID of the containing domain(s) as comma separated string, public for public offerings", + description = "The ID of the containing domain(s) as comma separated string, public for public offerings", since = "4.13", length = 4096) private String domainIds; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.STRING, - description = "the ID of the containing zone(s) as comma separated string, all for all zones offerings", + description = "The ID of the containing zone(s) as comma separated string, all for all zones offerings", + length = 4096, since = "4.13") private String zoneIds; @Parameter(name = ApiConstants.TAGS, type = CommandType.STRING, - description = "comma-separated list of tags for the disk offering, tags should match with existing storage pool tags", + description = "Comma-separated list of tags for the disk offering, tags should match with existing storage pool tags", since = "4.15") private String tags; - @Parameter(name = ApiConstants.BYTES_READ_RATE, type = CommandType.LONG, description = "bytes read rate of the disk offering", since = "4.15") + @Parameter(name = ApiConstants.BYTES_READ_RATE, type = CommandType.LONG, description = "Bytes read rate of the disk offering", since = "4.15") private Long bytesReadRate; - @Parameter(name = ApiConstants.BYTES_READ_RATE_MAX, type = CommandType.LONG, description = "burst bytes read rate of the disk offering", since = "4.15") + @Parameter(name = ApiConstants.BYTES_READ_RATE_MAX, type = CommandType.LONG, description = "Burst bytes read rate of the disk offering", since = "4.15") private Long bytesReadRateMax; - @Parameter(name = ApiConstants.BYTES_READ_RATE_MAX_LENGTH, type = CommandType.LONG, description = "length (in seconds) of the burst", since = "4.15") + @Parameter(name = ApiConstants.BYTES_READ_RATE_MAX_LENGTH, type = CommandType.LONG, description = "Length (in seconds) of the burst", since = "4.15") private Long bytesReadRateMaxLength; - @Parameter(name = ApiConstants.BYTES_WRITE_RATE, type = CommandType.LONG, description = "bytes write rate of the disk offering", since = "4.15") + @Parameter(name = ApiConstants.BYTES_WRITE_RATE, type = CommandType.LONG, description = "Bytes write rate of the disk offering", since = "4.15") private Long bytesWriteRate; - @Parameter(name = ApiConstants.BYTES_WRITE_RATE_MAX, type = CommandType.LONG, description = "burst bytes write rate of the disk offering", since = "4.15") + @Parameter(name = ApiConstants.BYTES_WRITE_RATE_MAX, type = CommandType.LONG, description = "Burst bytes write rate of the disk offering", since = "4.15") private Long bytesWriteRateMax; - @Parameter(name = ApiConstants.BYTES_WRITE_RATE_MAX_LENGTH, type = CommandType.LONG, description = "length (in seconds) of the burst", since = "4.15") + @Parameter(name = ApiConstants.BYTES_WRITE_RATE_MAX_LENGTH, type = CommandType.LONG, description = "Length (in seconds) of the burst", since = "4.15") private Long bytesWriteRateMaxLength; - @Parameter(name = ApiConstants.IOPS_READ_RATE, type = CommandType.LONG, description = "io requests read rate of the disk offering", since = "4.15") + @Parameter(name = ApiConstants.IOPS_READ_RATE, type = CommandType.LONG, description = "I/O requests read rate of the disk offering", since = "4.15") private Long iopsReadRate; - @Parameter(name = ApiConstants.IOPS_READ_RATE_MAX, type = CommandType.LONG, description = "burst requests read rate of the disk offering", since = "4.15") + @Parameter(name = ApiConstants.IOPS_READ_RATE_MAX, type = CommandType.LONG, description = "Burst requests read rate of the disk offering", since = "4.15") private Long iopsReadRateMax; - @Parameter(name = ApiConstants.IOPS_READ_RATE_MAX_LENGTH, type = CommandType.LONG, description = "length (in seconds) of the burst", since = "4.15") + @Parameter(name = ApiConstants.IOPS_READ_RATE_MAX_LENGTH, type = CommandType.LONG, description = "Length (in seconds) of the burst", since = "4.15") private Long iopsReadRateMaxLength; - @Parameter(name = ApiConstants.IOPS_WRITE_RATE, type = CommandType.LONG, description = "io requests write rate of the disk offering", since = "4.15") + @Parameter(name = ApiConstants.IOPS_WRITE_RATE, type = CommandType.LONG, description = "I/O requests write rate of the disk offering", since = "4.15") private Long iopsWriteRate; - @Parameter(name = ApiConstants.IOPS_WRITE_RATE_MAX, type = CommandType.LONG, description = "burst io requests write rate of the disk offering", since = "4.15") + @Parameter(name = ApiConstants.IOPS_WRITE_RATE_MAX, type = CommandType.LONG, description = "Burst io requests write rate of the disk offering", since = "4.15") private Long iopsWriteRateMax; - @Parameter(name = ApiConstants.IOPS_WRITE_RATE_MAX_LENGTH, type = CommandType.LONG, description = "length (in seconds) of the burst", since = "4.15") + @Parameter(name = ApiConstants.IOPS_WRITE_RATE_MAX_LENGTH, type = CommandType.LONG, description = "Length (in seconds) of the burst", since = "4.15") private Long iopsWriteRateMaxLength; - @Parameter(name = ApiConstants.CACHE_MODE, type = CommandType.STRING, description = "the cache mode to use for this disk offering", since = "4.15") + @Parameter(name = ApiConstants.CACHE_MODE, type = CommandType.STRING, description = "The cache mode to use for this disk offering", since = "4.15") private String cacheMode; @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "state of the disk offering") @@ -151,63 +150,11 @@ public Boolean getDisplayOffering() { } public List getDomainIds() { - List validDomainIds = new ArrayList<>(); - if (StringUtils.isNotEmpty(domainIds)) { - if (domainIds.contains(",")) { - String[] domains = domainIds.split(","); - for (String domain : domains) { - Domain validDomain = _entityMgr.findByUuid(Domain.class, domain.trim()); - if (validDomain != null) { - validDomainIds.add(validDomain.getId()); - } else { - throw new InvalidParameterValueException("Failed to create disk offering because invalid domain has been specified."); - } - } - } else { - domainIds = domainIds.trim(); - if (!domainIds.matches("public")) { - Domain validDomain = _entityMgr.findByUuid(Domain.class, domainIds.trim()); - if (validDomain != null) { - validDomainIds.add(validDomain.getId()); - } else { - throw new InvalidParameterValueException("Failed to create disk offering because invalid domain has been specified."); - } - } - } - } else { - validDomainIds.addAll(_configService.getDiskOfferingDomains(id)); - } - return validDomainIds; + return resolveDomainIds(domainIds, id, _configService::getDiskOfferingDomains, "disk offering"); } public List getZoneIds() { - List validZoneIds = new ArrayList<>(); - if (StringUtils.isNotEmpty(zoneIds)) { - if (zoneIds.contains(",")) { - String[] zones = zoneIds.split(","); - for (String zone : zones) { - DataCenter validZone = _entityMgr.findByUuid(DataCenter.class, zone.trim()); - if (validZone != null) { - validZoneIds.add(validZone.getId()); - } else { - throw new InvalidParameterValueException("Failed to create disk offering because invalid zone has been specified."); - } - } - } else { - zoneIds = zoneIds.trim(); - if (!zoneIds.matches("all")) { - DataCenter validZone = _entityMgr.findByUuid(DataCenter.class, zoneIds.trim()); - if (validZone != null) { - validZoneIds.add(validZone.getId()); - } else { - throw new InvalidParameterValueException("Failed to create disk offering because invalid zone has been specified."); - } - } - } - } else { - validZoneIds.addAll(_configService.getDiskOfferingZones(id)); - } - return validZoneIds; + return resolveZoneIds(zoneIds, id, _configService::getDiskOfferingZones, "disk offering"); } public String getTags() { 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 7f66ac7058a1..8e37499c95ed 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 @@ -16,7 +16,6 @@ // under the License. package org.apache.cloudstack.api.command.admin.offering; -import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -28,19 +27,18 @@ import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.offering.DomainAndZoneIdResolver; import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.commons.lang3.EnumUtils; import org.apache.commons.lang3.StringUtils; -import com.cloud.dc.DataCenter; -import com.cloud.domain.Domain; import com.cloud.exception.InvalidParameterValueException; import com.cloud.offering.ServiceOffering; import com.cloud.user.Account; @APICommand(name = "updateServiceOffering", description = "Updates a service offering.", responseObject = ServiceOfferingResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class UpdateServiceOfferingCmd extends BaseCmd { +public class UpdateServiceOfferingCmd extends BaseCmd implements DomainAndZoneIdResolver { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// @@ -49,39 +47,40 @@ public class UpdateServiceOfferingCmd extends BaseCmd { type = CommandType.UUID, entityType = ServiceOfferingResponse.class, required = true, - description = "the ID of the service offering to be updated") + description = "The ID of the service offering to be updated") private Long id; - @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "the display text of the service offering to be updated") + @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "The display text of the service offering to be updated") private String displayText; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the service offering to be updated") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the service offering to be updated") private String serviceOfferingName; - @Parameter(name = ApiConstants.SORT_KEY, type = CommandType.INTEGER, description = "sort key of the service offering, integer") + @Parameter(name = ApiConstants.SORT_KEY, type = CommandType.INTEGER, description = "Sort key of the service offering, integer") private Integer sortKey; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.STRING, - description = "the ID of the containing domain(s) as comma separated string, public for public offerings", + description = "The ID of the containing domain(s) as comma separated string, public for public offerings", length = 4096) private String domainIds; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.STRING, - description = "the ID of the containing zone(s) as comma separated string, all for all zones offerings", + description = "The ID of the containing zone(s) as comma separated string, all for all zones offerings", + length = 4096, since = "4.13") private String zoneIds; @Parameter(name = ApiConstants.STORAGE_TAGS, type = CommandType.STRING, - description = "comma-separated list of tags for the service offering, tags should match with existing storage pool tags", + description = "Comma-separated list of tags for the service offering, tags should match with existing storage pool tags", since = "4.16") private String storageTags; @Parameter(name = ApiConstants.HOST_TAGS, type = CommandType.STRING, - description = "the host tag for this service offering.", + description = "The host tag for this service offering.", since = "4.16") private String hostTags; @@ -101,6 +100,14 @@ public class UpdateServiceOfferingCmd extends BaseCmd { since = "4.21.0") private Map externalDetails; + @Parameter(name = ApiConstants.CLEAN_UP_EXTERNAL_DETAILS, + type = CommandType.BOOLEAN, + description = "Optional boolean field, which indicates if external details should be cleaned up or not " + + "(If set to true, external details removed for this offering, externaldetails field ignored; " + + "if false or not set, no action)", + since = "4.22.0") + protected Boolean cleanupExternalDetails; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -122,63 +129,11 @@ public Integer getSortKey() { } public List getDomainIds() { - List validDomainIds = new ArrayList<>(); - if (StringUtils.isNotEmpty(domainIds)) { - if (domainIds.contains(",")) { - String[] domains = domainIds.split(","); - for (String domain : domains) { - Domain validDomain = _entityMgr.findByUuid(Domain.class, domain.trim()); - if (validDomain != null) { - validDomainIds.add(validDomain.getId()); - } else { - throw new InvalidParameterValueException("Failed to create service offering because invalid domain has been specified."); - } - } - } else { - domainIds = domainIds.trim(); - if (!domainIds.matches("public")) { - Domain validDomain = _entityMgr.findByUuid(Domain.class, domainIds.trim()); - if (validDomain != null) { - validDomainIds.add(validDomain.getId()); - } else { - throw new InvalidParameterValueException("Failed to create service offering because invalid domain has been specified."); - } - } - } - } else { - validDomainIds.addAll(_configService.getServiceOfferingDomains(id)); - } - return validDomainIds; + return resolveDomainIds(domainIds, id, _configService::getServiceOfferingDomains, "service offering"); } public List getZoneIds() { - List validZoneIds = new ArrayList<>(); - if (StringUtils.isNotEmpty(zoneIds)) { - if (zoneIds.contains(",")) { - String[] zones = zoneIds.split(","); - for (String zone : zones) { - DataCenter validZone = _entityMgr.findByUuid(DataCenter.class, zone.trim()); - if (validZone != null) { - validZoneIds.add(validZone.getId()); - } else { - throw new InvalidParameterValueException("Failed to create service offering because invalid zone has been specified."); - } - } - } else { - zoneIds = zoneIds.trim(); - if (!zoneIds.matches("all")) { - DataCenter validZone = _entityMgr.findByUuid(DataCenter.class, zoneIds.trim()); - if (validZone != null) { - validZoneIds.add(validZone.getId()); - } else { - throw new InvalidParameterValueException("Failed to create service offering because invalid zone has been specified."); - } - } - } - } else { - validZoneIds.addAll(_configService.getServiceOfferingZones(id)); - } - return validZoneIds; + return resolveZoneIds(zoneIds, id, _configService::getServiceOfferingZones, "service offering"); } public String getStorageTags() { @@ -205,6 +160,10 @@ public Map getExternalDetails() { return convertExternalDetailsToMap(externalDetails); } + public boolean isCleanupExternalDetails() { + return Boolean.TRUE.equals(cleanupExternalDetails); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/ChangeOutOfBandManagementPasswordCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/ChangeOutOfBandManagementPasswordCmd.java index e2c31d6cf072..b9a729bc1b77 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/ChangeOutOfBandManagementPasswordCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/ChangeOutOfBandManagementPasswordCmd.java @@ -53,10 +53,10 @@ public class ChangeOutOfBandManagementPasswordCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, - validations = {ApiArgValidator.PositiveNumber}, description = "the ID of the host") + validations = {ApiArgValidator.PositiveNumber}, description = "The ID of the host") private Long hostId; - @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, description = "the new host management interface password of maximum length 16, if none is provided a random password would be used") + @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, description = "The new host management interface password of maximum length 16, if none is provided a random password would be used") private String password; ///////////////////////////////////////////////////// @@ -70,7 +70,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to find host by ID: " + getHostId()); } - CallContext.current().setEventDetails("Host Id: " + host.getId() + " Password: " + getPassword().charAt(0) + "****"); + CallContext.current().setEventDetails("Host ID: " + host.getUuid() + " Password: " + getPassword().charAt(0) + "****"); CallContext.current().putContextParameter(Host.class, host.getUuid()); final OutOfBandManagementResponse response = outOfBandManagementService.changePassword(host, getPassword()); @@ -101,7 +101,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "change out-of-band management password for host: " + getHostId(); + return "Changing out-of-band management password for host with ID: " + getResourceUuid(ApiConstants.HOST_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/ConfigureOutOfBandManagementCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/ConfigureOutOfBandManagementCmd.java index 157d3c627db5..4052539a51d7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/ConfigureOutOfBandManagementCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/ConfigureOutOfBandManagementCmd.java @@ -54,22 +54,22 @@ public class ConfigureOutOfBandManagementCmd extends BaseCmd { ///////////////////////////////////////////////////// @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, - validations = {ApiArgValidator.PositiveNumber}, description = "the ID of the host") + validations = {ApiArgValidator.PositiveNumber}, description = "The ID of the host") private Long hostId; - @Parameter(name = ApiConstants.DRIVER, type = CommandType.STRING, required = true, description = "the host management interface driver, for example: ipmitool") + @Parameter(name = ApiConstants.DRIVER, type = CommandType.STRING, required = true, description = "The host management interface driver, for example: ipmitool") private String driver; - @Parameter(name = ApiConstants.ADDRESS, type = CommandType.STRING, required = true, description = "the host management interface IP address") + @Parameter(name = ApiConstants.ADDRESS, type = CommandType.STRING, required = true, description = "The host management interface IP address") private String address; - @Parameter(name = ApiConstants.PORT, type = CommandType.STRING, required = true, description = "the host management interface port") + @Parameter(name = ApiConstants.PORT, type = CommandType.STRING, required = true, description = "The host management interface port") private String port; - @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, required = true, description = "the host management interface user") + @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, required = true, description = "The host management interface user") private String username; - @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, required = true, description = "the host management interface password") + @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, required = true, description = "The host management interface password") private String password; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/DisableOutOfBandManagementForClusterCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/DisableOutOfBandManagementForClusterCmd.java index 445e7b92665c..5b0f3a802071 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/DisableOutOfBandManagementForClusterCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/DisableOutOfBandManagementForClusterCmd.java @@ -54,7 +54,7 @@ public class DisableOutOfBandManagementForClusterCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// @Parameter(name = ApiConstants.CLUSTER_ID, type = BaseCmd.CommandType.UUID, required = true, entityType = ClusterResponse.class, - validations = {ApiArgValidator.PositiveNumber}, description = "the ID of the cluster") + validations = {ApiArgValidator.PositiveNumber}, description = "The ID of the cluster") private Long clusterId; ///////////////////////////////////////////////////// @@ -70,7 +70,7 @@ final public void execute() throws ResourceUnavailableException, InsufficientCap OutOfBandManagementResponse response = outOfBandManagementService.disableOutOfBandManagement(cluster); - CallContext.current().setEventDetails("Cluster Id:" + cluster.getId() + " out-of-band management enabled: false"); + CallContext.current().setEventDetails("Cluster ID:" + cluster.getUuid() + " out-of-band management enabled: false"); CallContext.current().putContextParameter(Cluster.class, cluster.getUuid()); response.setResponseName(getCommandName()); @@ -93,7 +93,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "disable out-of-band management password for cluster: " + getClusterId(); + return "Disabling out-of-band management password for cluster with ID: " + getResourceUuid(ApiConstants.CLUSTER_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/DisableOutOfBandManagementForHostCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/DisableOutOfBandManagementForHostCmd.java index 7e4444e93fbd..c70622134531 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/DisableOutOfBandManagementForHostCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/DisableOutOfBandManagementForHostCmd.java @@ -54,7 +54,7 @@ public class DisableOutOfBandManagementForHostCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// @Parameter(name = ApiConstants.HOST_ID, type = BaseCmd.CommandType.UUID, required = true, entityType = HostResponse.class, - validations = {ApiArgValidator.PositiveNumber}, description = "the ID of the host") + validations = {ApiArgValidator.PositiveNumber}, description = "The ID of the host") private Long hostId; ///////////////////////////////////////////////////// @@ -70,7 +70,7 @@ final public void execute() throws ResourceUnavailableException, InsufficientCap OutOfBandManagementResponse response = outOfBandManagementService.disableOutOfBandManagement(host); - CallContext.current().setEventDetails("Host Id:" + host.getId() + " out-of-band management enabled: false"); + CallContext.current().setEventDetails("Host ID:" + host.getUuid() + " out-of-band management enabled: false"); CallContext.current().putContextParameter(Host.class, host.getUuid()); response.setId(host.getUuid()); @@ -94,7 +94,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "disable out-of-band management password for host: " + getHostId(); + return "Disabling out-of-band management password for host with ID: " + getResourceUuid(ApiConstants.HOST_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/DisableOutOfBandManagementForZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/DisableOutOfBandManagementForZoneCmd.java index 2028f8fc2a07..2f2750580516 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/DisableOutOfBandManagementForZoneCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/DisableOutOfBandManagementForZoneCmd.java @@ -54,7 +54,7 @@ public class DisableOutOfBandManagementForZoneCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// @Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, required = true, entityType = ZoneResponse.class, - validations = {ApiArgValidator.PositiveNumber}, description = "the ID of the zone") + validations = {ApiArgValidator.PositiveNumber}, description = "The ID of the zone") private Long zoneId; ///////////////////////////////////////////////////// @@ -70,7 +70,7 @@ final public void execute() throws ResourceUnavailableException, InsufficientCap OutOfBandManagementResponse response = outOfBandManagementService.disableOutOfBandManagement(zone); - CallContext.current().setEventDetails("Zone Id:" + zone.getId() + " out-of-band management enabled: false"); + CallContext.current().setEventDetails("Zone ID:" + zone.getUuid() + " out-of-band management enabled: false"); CallContext.current().putContextParameter(DataCenter.class, zone.getUuid()); response.setResponseName(getCommandName()); @@ -93,7 +93,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "disable out-of-band management password for zone: " + getZoneId(); + return "Disabling out-of-band management password for zone with ID: " + getResourceUuid(ApiConstants.ZONE_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/EnableOutOfBandManagementForClusterCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/EnableOutOfBandManagementForClusterCmd.java index 549743b31728..4419ed18ee59 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/EnableOutOfBandManagementForClusterCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/EnableOutOfBandManagementForClusterCmd.java @@ -54,7 +54,7 @@ public class EnableOutOfBandManagementForClusterCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// @Parameter(name = ApiConstants.CLUSTER_ID, type = BaseCmd.CommandType.UUID, required = true, entityType = ClusterResponse.class, - validations = {ApiArgValidator.PositiveNumber}, description = "the ID of the cluster") + validations = {ApiArgValidator.PositiveNumber}, description = "The ID of the cluster") private Long clusterId; ///////////////////////////////////////////////////// @@ -70,7 +70,7 @@ final public void execute() throws ResourceUnavailableException, InsufficientCap OutOfBandManagementResponse response = outOfBandManagementService.enableOutOfBandManagement(cluster); - CallContext.current().setEventDetails("Cluster Id:" + cluster.getId() + " out-of-band management enabled: true"); + CallContext.current().setEventDetails("Cluster ID:" + cluster.getUuid() + " out-of-band management enabled: true"); CallContext.current().putContextParameter(Cluster.class, cluster.getUuid()); response.setResponseName(getCommandName()); @@ -93,7 +93,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "enable out-of-band management password for cluster: " + getClusterId(); + return "Enabling out-of-band management password for cluster with ID: " + getResourceUuid(ApiConstants.CLUSTER_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/EnableOutOfBandManagementForHostCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/EnableOutOfBandManagementForHostCmd.java index 834181a5e1c5..b3f2e8aaf821 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/EnableOutOfBandManagementForHostCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/EnableOutOfBandManagementForHostCmd.java @@ -54,7 +54,7 @@ public class EnableOutOfBandManagementForHostCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// @Parameter(name = ApiConstants.HOST_ID, type = BaseCmd.CommandType.UUID, required = true, entityType = HostResponse.class, - validations = {ApiArgValidator.PositiveNumber}, description = "the ID of the host") + validations = {ApiArgValidator.PositiveNumber}, description = "The ID of the host") private Long hostId; ///////////////////////////////////////////////////// @@ -70,7 +70,7 @@ final public void execute() throws ResourceUnavailableException, InsufficientCap OutOfBandManagementResponse response = outOfBandManagementService.enableOutOfBandManagement(host); - CallContext.current().setEventDetails("Host Id:" + host.getId() + " out-of-band management enabled: true"); + CallContext.current().setEventDetails("Host ID:" + host.getUuid() + " out-of-band management enabled: true"); CallContext.current().putContextParameter(Host.class, host.getUuid()); response.setId(host.getUuid()); @@ -94,7 +94,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "enable out-of-band management password for host: " + getHostId(); + return "Enabling out-of-band management password for host with ID: " + getResourceUuid(ApiConstants.HOST_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/EnableOutOfBandManagementForZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/EnableOutOfBandManagementForZoneCmd.java index de4c4d801de1..8f6eb2dc3500 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/EnableOutOfBandManagementForZoneCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/EnableOutOfBandManagementForZoneCmd.java @@ -54,7 +54,7 @@ public class EnableOutOfBandManagementForZoneCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// @Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, required = true, entityType = ZoneResponse.class, - validations = {ApiArgValidator.PositiveNumber}, description = "the ID of the zone") + validations = {ApiArgValidator.PositiveNumber}, description = "The ID of the zone") private Long zoneId; ///////////////////////////////////////////////////// @@ -70,7 +70,7 @@ final public void execute() throws ResourceUnavailableException, InsufficientCap OutOfBandManagementResponse response = outOfBandManagementService.enableOutOfBandManagement(zone); - CallContext.current().setEventDetails("Zone Id:" + zone.getId() + " out-of-band management enabled: true"); + CallContext.current().setEventDetails("Zone ID:" + zone.getUuid() + " out-of-band management enabled: true"); CallContext.current().putContextParameter(DataCenter.class, zone.getUuid()); response.setResponseName(getCommandName()); @@ -93,7 +93,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "enable out-of-band management password for zone: " + getZoneId(); + return "Enabling out-of-band management password for zone with ID: " + getResourceUuid(ApiConstants.ZONE_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/IssueOutOfBandManagementPowerActionCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/IssueOutOfBandManagementPowerActionCmd.java index 97a813c9d476..bba3fee6ceca 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/IssueOutOfBandManagementPowerActionCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/outofbandmanagement/IssueOutOfBandManagementPowerActionCmd.java @@ -53,14 +53,14 @@ public class IssueOutOfBandManagementPowerActionCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, - validations = {ApiArgValidator.PositiveNumber}, description = "the ID of the host") + validations = {ApiArgValidator.PositiveNumber}, description = "The ID of the host") private Long hostId; - @Parameter(name = ApiConstants.TIMEOUT, type = CommandType.LONG, description = "optional operation timeout in seconds that overrides the global or cluster-level out-of-band management timeout setting") + @Parameter(name = ApiConstants.TIMEOUT, type = CommandType.LONG, description = "Optional operation timeout in seconds that overrides the global or cluster-level out-of-band management timeout setting") private Long actionTimeout; @Parameter(name = ApiConstants.ACTION, type = CommandType.STRING, required = true, - description = "out-of-band management power actions, valid actions are: ON, OFF, CYCLE, RESET, SOFT, STATUS") + description = "Out-of-band management power actions, valid actions are: ON, OFF, CYCLE, RESET, SOFT, STATUS") private String powerAction; ///////////////////////////////////////////////////// @@ -75,7 +75,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE } final PowerOperation powerOperation = PowerOperation.valueOf(getPowerAction()); - CallContext.current().setEventDetails("Host Id: " + host.getId() + " Action: " + powerOperation.toString()); + CallContext.current().setEventDetails("Host ID: " + host.getUuid() + " Action: " + powerOperation.toString()); CallContext.current().putContextParameter(Host.class, host.getUuid()); final OutOfBandManagementResponse response = outOfBandManagementService.executePowerOperation(host, powerOperation, getActionTimeout()); @@ -107,7 +107,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "issue out-out-band management power action: " + getPowerAction() + " on host: " + getHostId(); + return "Issuing out-out-band management power action: " + getPowerAction() + " to host: " + getResourceUuid(ApiConstants.HOST_ID); } @Override 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 36ad00e6e4ab..6a178b64d876 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 @@ -40,26 +40,26 @@ public class CreatePodCmd extends BaseCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the Pod") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "The name of the Pod") private String podName; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, - description = "the Zone ID in which the Pod will be created") + description = "The Zone ID in which the Pod will be created") private Long zoneId; - @Parameter(name = ApiConstants.START_IP, type = CommandType.STRING, description = "the starting IP address for the Pod") + @Parameter(name = ApiConstants.START_IP, type = CommandType.STRING, description = "The starting IP address for the Pod") private String startIp; - @Parameter(name = ApiConstants.END_IP, type = CommandType.STRING, description = "the ending IP address for the Pod") + @Parameter(name = ApiConstants.END_IP, type = CommandType.STRING, description = "The ending IP address for the Pod") private String endIp; - @Parameter(name = ApiConstants.NETMASK, type = CommandType.STRING, description = "the netmask for the Pod") + @Parameter(name = ApiConstants.NETMASK, type = CommandType.STRING, description = "The netmask for the Pod") private String netmask; - @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, description = "the gateway for the Pod") + @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, description = "The gateway for the Pod") private String gateway; @Parameter(name = ApiConstants.ALLOCATION_STATE, type = CommandType.STRING, description = "Allocation state of this Pod for allocation of new resources") diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/DeletePodCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/DeletePodCmd.java index c1de800d745b..953fcc91fa71 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/DeletePodCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/DeletePodCmd.java @@ -38,7 +38,7 @@ public class DeletePodCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = PodResponse.class, required = true, description = "the ID of the Pod") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = PodResponse.class, required = true, description = "The ID of the Pod") private Long id; ///////////////////////////////////////////////////// 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 264e5263c4d7..a19bc3caf8b5 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 @@ -40,19 +40,19 @@ public class ListPodsByCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = PodResponse.class, description = "list Pods by ID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = PodResponse.class, description = "List Pods by ID") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "list Pods by name") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "List Pods by name") private String podName; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "list Pods by Zone ID") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "List Pods by Zone ID") private Long zoneId; - @Parameter(name = ApiConstants.ALLOCATION_STATE, type = CommandType.STRING, description = "list pods by allocation state") + @Parameter(name = ApiConstants.ALLOCATION_STATE, type = CommandType.STRING, description = "List pods by allocation state") private String allocationState; - @Parameter(name = ApiConstants.SHOW_CAPACITIES, type = CommandType.BOOLEAN, description = "flag to display the capacity of the pods") + @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, diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/UpdatePodCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/UpdatePodCmd.java index 7dae6f4c7cf0..24a549c623ad 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/UpdatePodCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/UpdatePodCmd.java @@ -38,22 +38,22 @@ public class UpdatePodCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = PodResponse.class, required = true, description = "the ID of the Pod") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = PodResponse.class, required = true, description = "The ID of the Pod") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the Pod") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the Pod") private String podName; - @Parameter(name = ApiConstants.START_IP, type = CommandType.STRING, description = "the starting IP address for the Pod") + @Parameter(name = ApiConstants.START_IP, type = CommandType.STRING, description = "The starting IP address for the Pod") private String startIp; - @Parameter(name = ApiConstants.END_IP, type = CommandType.STRING, description = "the ending IP address for the Pod") + @Parameter(name = ApiConstants.END_IP, type = CommandType.STRING, description = "The ending IP address for the Pod") private String endIp; - @Parameter(name = ApiConstants.NETMASK, type = CommandType.STRING, description = "the netmask of the Pod") + @Parameter(name = ApiConstants.NETMASK, type = CommandType.STRING, description = "The netmask of the Pod") private String netmask; - @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, description = "the gateway for the Pod") + @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, description = "The gateway for the Pod") private String gateway; @Parameter(name = ApiConstants.ALLOCATION_STATE, type = CommandType.STRING, description = "Allocation state of this cluster for allocation of new resources") diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/region/CreatePortableIpRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/region/CreatePortableIpRangeCmd.java index fd103c838309..1142bdd3f43c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/region/CreatePortableIpRangeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/region/CreatePortableIpRangeCmd.java @@ -36,7 +36,7 @@ @APICommand(name = "createPortableIpRange", responseObject = PortableIpRangeResponse.class, - description = "adds a range of portable public IP's to a region", + description = "Adds a range of portable public IP's to a region", since = "4.2.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -51,16 +51,16 @@ public class CreatePortableIpRangeCmd extends BaseAsyncCreateCmd { @Parameter(name = ApiConstants.REGION_ID, type = CommandType.INTEGER, entityType = RegionResponse.class, required = true, description = "Id of the Region") private Integer regionId; - @Parameter(name = ApiConstants.START_IP, type = CommandType.STRING, required = true, description = "the beginning IP address in the portable IP range") + @Parameter(name = ApiConstants.START_IP, type = CommandType.STRING, required = true, description = "The beginning IP address in the portable IP range") private String startIp; - @Parameter(name = ApiConstants.END_IP, type = CommandType.STRING, required = true, description = "the ending IP address in the portable IP range") + @Parameter(name = ApiConstants.END_IP, type = CommandType.STRING, required = true, description = "The ending IP address in the portable IP range") private String endIp; - @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, required = true, description = "the gateway for the portable IP range") + @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, required = true, description = "The gateway for the portable IP range") private String gateway; - @Parameter(name = ApiConstants.NETMASK, type = CommandType.STRING, required = true, description = "the netmask of the portable IP range") + @Parameter(name = ApiConstants.NETMASK, type = CommandType.STRING, required = true, description = "The netmask of the portable IP range") private String netmask; @Parameter(name = ApiConstants.VLAN, type = CommandType.STRING, description = "VLAN id, if not specified defaulted to untagged") diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/region/DeletePortableIpRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/region/DeletePortableIpRangeCmd.java index 3ff46fcc94d5..ac8fe66a8144 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/region/DeletePortableIpRangeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/region/DeletePortableIpRangeCmd.java @@ -31,7 +31,7 @@ import com.cloud.event.EventTypes; import com.cloud.user.Account; -@APICommand(name = "deletePortableIpRange", description = "deletes a range of portable public IP's associated with a region", responseObject = SuccessResponse.class, +@APICommand(name = "deletePortableIpRange", description = "Deletes a range of portable public IP's associated with a region", responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DeletePortableIpRangeCmd extends BaseAsyncCmd { @@ -83,7 +83,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "deleting a portable public ip range"; + return "Deleting a portable public ip range with ID: " + getResourceUuid(ApiConstants.ID) ; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/region/ListPortableIpRangesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/region/ListPortableIpRangesCmd.java index e654da6df449..92a1cc3822e9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/region/ListPortableIpRangesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/region/ListPortableIpRangesCmd.java @@ -32,7 +32,7 @@ import com.cloud.user.Account; -@APICommand(name = "listPortableIpRanges", description = "list portable IP ranges", responseObject = PortableIpRangeResponse.class, +@APICommand(name = "listPortableIpRanges", description = "List portable IP ranges", responseObject = PortableIpRangeResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListPortableIpRangesCmd extends BaseListCmd { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/region/UpdateRegionCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/region/UpdateRegionCmd.java index 4267f6a2c286..ec5bfdabf5ec 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/region/UpdateRegionCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/region/UpdateRegionCmd.java @@ -42,10 +42,10 @@ public class UpdateRegionCmd extends BaseCmd { @Parameter(name = ApiConstants.ID, type = CommandType.INTEGER, required = true, description = "Id of region to update") private Integer id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "updates region with this name") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Updates region with this name") private String regionName; - @Parameter(name = ApiConstants.END_POINT, type = CommandType.STRING, description = "updates region with this end point") + @Parameter(name = ApiConstants.END_POINT, type = CommandType.STRING, description = "Updates region with this end point") private String endPoint; @Inject diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ArchiveAlertsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ArchiveAlertsCmd.java index dc8c15cf09df..e495a1683292 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ArchiveAlertsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ArchiveAlertsCmd.java @@ -46,18 +46,18 @@ public class ArchiveAlertsCmd extends BaseCmd { type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AlertResponse.class, - description = "the IDs of the alerts") + description = "The IDs of the alerts") private List ids; - @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, description = "end date range to archive alerts" + @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, description = "End date range to archive alerts" + " (including) this date (use format \"yyyy-MM-dd\" or the new format \"yyyy-MM-ddThh:mm:ss\")") private Date endDate; - @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, description = "start date range to archive alerts" + @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, description = "Start date range to archive alerts" + " (including) this date (use format \"yyyy-MM-dd\" or the new format \"yyyy-MM-ddThh:mm:ss\")") private Date startDate; - @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "archive by alert type") + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "Archive by alert type") private String type; // /////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/CleanVMReservationsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/CleanVMReservationsCmd.java index 1ae8c9441233..ba1b2be4008f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/CleanVMReservationsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/CleanVMReservationsCmd.java @@ -27,7 +27,7 @@ import com.cloud.event.EventTypes; import com.cloud.user.Account; -@APICommand(name = "cleanVMReservations", description = "Cleanups VM reservations in the database.", responseObject = SuccessResponse.class, +@APICommand(name = "cleanVMReservations", description = "Cleanups Instance reservations in the database.", responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CleanVMReservationsCmd extends BaseAsyncCmd { @@ -63,7 +63,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "cleaning vm reservations in database"; + return "Cleaning Instance reservations in database"; } @Override @@ -73,7 +73,7 @@ public void execute() { SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); } catch (Exception ex) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to clean vm reservations"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to clean Instance reservations"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/DeleteAlertsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/DeleteAlertsCmd.java index 9262a120f72c..f59a16d83733 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/DeleteAlertsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/DeleteAlertsCmd.java @@ -46,18 +46,18 @@ public class DeleteAlertsCmd extends BaseCmd { type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AlertResponse.class, - description = "the IDs of the alerts") + description = "The IDs of the alerts") private List ids; - @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, description = "end date range to delete alerts" + @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, description = "End date range to delete alerts" + " (including) this date (use format \"yyyy-MM-dd\" or the new format \"yyyy-MM-ddThh:mm:ss\")") private Date endDate; - @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, description = "start date range to delete alerts" + @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, description = "Start date range to delete alerts" + " (including) this date (use format \"yyyy-MM-dd\" or the new format \"yyyy-MM-ddThh:mm:ss\")") private Date startDate; - @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "delete by alert type") + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "Delete by alert type") private String type; // /////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ListAlertTypesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ListAlertTypesCmd.java index e7bfbdbc6254..dcd4f2c89ef2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ListAlertTypesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ListAlertTypesCmd.java @@ -16,7 +16,10 @@ // under the License. package org.apache.cloudstack.api.command.admin.resource; -import com.cloud.user.Account; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + import org.apache.cloudstack.alert.AlertService; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.BaseCmd; @@ -24,9 +27,7 @@ import org.apache.cloudstack.api.response.AlertTypeResponse; import org.apache.cloudstack.api.response.ListResponse; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; +import com.cloud.user.Account; @APICommand(name = "listAlertTypes", description = "Lists all alerts types", responseObject = AlertResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -43,7 +44,8 @@ public void execute() { ListResponse response = new ListResponse<>(); List typeResponseList = new ArrayList<>(); for (AlertService.AlertType alertType : result) { - AlertTypeResponse alertResponse = new AlertTypeResponse(alertType.getType(), alertType.getName()); + AlertTypeResponse alertResponse = new AlertTypeResponse(alertType.getType(), alertType.getName(), + alertType.isRepetitionAllowed()); alertResponse.setObjectName("alerttype"); typeResponseList.add(alertResponse); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ListAlertsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ListAlertsCmd.java index 64cf691e6a73..e88896703238 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ListAlertsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ListAlertsCmd.java @@ -39,13 +39,13 @@ public class ListAlertsCmd extends BaseListCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AlertResponse.class, description = "the ID of the alert") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AlertResponse.class, description = "The ID of the alert") private Long id; - @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "list by alert type") + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "List by alert type") private String type; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "list by alert name", since = "4.3") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "List by alert name", since = "4.3") private String name; // /////////////////////////////////////////////////// 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 f46cecdef504..ae98ca9dcd26 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 @@ -46,23 +46,23 @@ public class ListCapacityCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "lists capacity by the Zone ID") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "Lists capacity by the Zone ID") private Long zoneId; - @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "lists capacity by the Pod ID") + @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "Lists capacity by the Pod ID") private Long podId; @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, since = "3.0.0", - description = "lists capacity by the Cluster ID") + description = "Lists capacity by the Cluster ID") private Long clusterId; - @Parameter(name = ApiConstants.FETCH_LATEST, type = CommandType.BOOLEAN, since = "3.0.0", description = "recalculate capacities and fetch the latest") + @Parameter(name = ApiConstants.FETCH_LATEST, type = CommandType.BOOLEAN, since = "3.0.0", description = "Recalculate capacities and fetch the latest") private Boolean fetchLatest; - @Parameter(name = ApiConstants.TYPE, type = CommandType.INTEGER, description = "lists capacity by type" + "* CAPACITY_TYPE_MEMORY = 0" + "* CAPACITY_TYPE_CPU = 1" + @Parameter(name = ApiConstants.TYPE, type = CommandType.INTEGER, description = "Lists capacity by type" + "* CAPACITY_TYPE_MEMORY = 0" + "* CAPACITY_TYPE_CPU = 1" + "* CAPACITY_TYPE_STORAGE = 2" + "* CAPACITY_TYPE_STORAGE_ALLOCATED = 3" + "* CAPACITY_TYPE_VIRTUAL_NETWORK_PUBLIC_IP = 4" + "* CAPACITY_TYPE_PRIVATE_IP = 5" + "* CAPACITY_TYPE_SECONDARY_STORAGE = 6" + "* CAPACITY_TYPE_VLAN = 7" + "* CAPACITY_TYPE_DIRECT_ATTACHED_PUBLIC_IP = 8" + "* CAPACITY_TYPE_LOCAL_STORAGE = 9" + "* CAPACITY_TYPE_GPU = 19" + "* CAPACITY_TYPE_CPU_CORE = 90.") diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/StartRollingMaintenanceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/StartRollingMaintenanceCmd.java index 04fa1002611c..3869b9cdf957 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/StartRollingMaintenanceCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/StartRollingMaintenanceCmd.java @@ -60,31 +60,31 @@ public class StartRollingMaintenanceCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @Parameter(name = ApiConstants.POD_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, - entityType = PodResponse.class, description = "the IDs of the pods to start maintenance on") + entityType = PodResponse.class, description = "The IDs of the pods to start maintenance on") private List podIds; @Parameter(name = ApiConstants.CLUSTER_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, - entityType = ClusterResponse.class, description = "the IDs of the clusters to start maintenance on") + entityType = ClusterResponse.class, description = "The IDs of the clusters to start maintenance on") private List clusterIds; @Parameter(name = ApiConstants.ZONE_ID_LIST, type = CommandType.LIST, collectionType = CommandType.UUID, - entityType = ZoneResponse.class, description = "the IDs of the zones to start maintenance on") + entityType = ZoneResponse.class, description = "The IDs of the zones to start maintenance on") private List zoneIds; @Parameter(name = ApiConstants.HOST_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, - entityType = HostResponse.class, description = "the IDs of the hosts to start maintenance on") + entityType = HostResponse.class, description = "The IDs of the hosts to start maintenance on") private List hostIds; @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, - description = "if rolling mechanism should continue in case of an error") + description = "If rolling mechanism should continue in case of an error") private Boolean forced; @Parameter(name = ApiConstants.PAYLOAD, type = CommandType.STRING, - description = "the command to execute while hosts are on maintenance") + description = "The command to execute while hosts are on maintenance") private String payload; @Parameter(name = ApiConstants.TIMEOUT, type = CommandType.INTEGER, - description = "optional operation timeout (in seconds) that overrides the global timeout setting") + description = "Optional operation timeout (in seconds) that overrides the global timeout setting") private Integer timeout; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/UploadCustomCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/UploadCustomCertificateCmd.java index c5ae6890c3e5..7144bbaa6015 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/UploadCustomCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/UploadCustomCertificateCmd.java @@ -84,7 +84,7 @@ public String getEventType() { @Override public String getEventDescription() { - return ("Uploading custom certificate to the db, and applying it to all the cpvms in the system"); + return ("Uploading custom certificate and applying it to all the CPVMs in the system"); } public static String getResultObjectName() { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/DeleteResourceIconCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/DeleteResourceIconCmd.java index e97a68bddcb6..6289879c1a27 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/DeleteResourceIconCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/DeleteResourceIconCmd.java @@ -31,7 +31,7 @@ import java.util.List; -@APICommand(name = "deleteResourceIcon", description = "deletes the resource icon from the specified resource(s)", +@APICommand(name = "deleteResourceIcon", description = "Deletes the resource icon from the specified resource(s)", responseObject = SuccessResponse.class, since = "4.16.0.0", entityType = {ResourceIcon.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin, RoleType.DomainAdmin, RoleType.ResourceAdmin, RoleType.User}) @@ -45,10 +45,10 @@ public class DeleteResourceIconCmd extends BaseCmd { type = BaseCmd.CommandType.LIST, required = true, collectionType = BaseCmd.CommandType.STRING, - description = "list of resources to upload the icon/image for") + description = "List of resources to upload the icon/image for") private List resourceIds; - @Parameter(name = ApiConstants.RESOURCE_TYPE, type = BaseCmd.CommandType.STRING, required = true, description = "type of the resource") + @Parameter(name = ApiConstants.RESOURCE_TYPE, type = BaseCmd.CommandType.STRING, required = true, description = "Type of the resource") private String resourceType; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/ListResourceIconCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/ListResourceIconCmd.java index 6cc3173cf155..7b9c9671d47a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/ListResourceIconCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/ListResourceIconCmd.java @@ -42,10 +42,10 @@ public class ListResourceIconCmd extends BaseCmd { type = BaseCmd.CommandType.LIST, required = true, collectionType = BaseCmd.CommandType.STRING, - description = "list of resources to upload the icon/image for") + description = "List of resources to upload the icon/image for") private List resourceIds; - @Parameter(name = ApiConstants.RESOURCE_TYPE, type = BaseCmd.CommandType.STRING, required = true, description = "type of the resource") + @Parameter(name = ApiConstants.RESOURCE_TYPE, type = BaseCmd.CommandType.STRING, required = true, description = "Type of the resource") private String resourceType; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/UploadResourceIconCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/UploadResourceIconCmd.java index 5a6acd961bf5..9201e345302c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/UploadResourceIconCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/UploadResourceIconCmd.java @@ -55,10 +55,10 @@ public class UploadResourceIconCmd extends BaseCmd { type = BaseCmd.CommandType.LIST, required = true, collectionType = BaseCmd.CommandType.STRING, - description = "list of resources to upload the icon/image for") + description = "List of resources to upload the icon/image for") private List resourceIds; - @Parameter(name = ApiConstants.RESOURCE_TYPE, type = BaseCmd.CommandType.STRING, required = true, description = "type of the resource") + @Parameter(name = ApiConstants.RESOURCE_TYPE, type = BaseCmd.CommandType.STRING, required = true, description = "Type of the resource") private String resourceType; @Parameter(name = ApiConstants.BASE64_IMAGE, type = BaseCmd.CommandType.STRING, required = true, diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ConfigureOvsElementCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ConfigureOvsElementCmd.java index 4a8c0bc3a3b9..474e13de32f3 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ConfigureOvsElementCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ConfigureOvsElementCmd.java @@ -48,7 +48,7 @@ public class ConfigureOvsElementCmd extends BaseAsyncCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = OvsProviderResponse.class, required = true, description = "the ID of the ovs provider") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = OvsProviderResponse.class, required = true, description = "The ID of the ovs provider") private Long id; @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, required = true, description = "Enabled/Disabled the service provider") @@ -93,7 +93,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "configuring ovs provider: " + id; + return "Configuring OVS provider with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -109,7 +109,7 @@ public Long getApiResourceId() { @Override public void execute() throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { - CallContext.current().setEventDetails("Ovs element: " + id); + CallContext.current().setEventDetails("OVS element: " + getResourceUuid(ApiConstants.ID)); OvsProvider result = _service.get(0).configure(this); if (result != null) { OvsProviderResponse ovsResponse = _responseGenerator diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ConfigureVirtualRouterElementCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ConfigureVirtualRouterElementCmd.java index aa119f3aca75..ba0110f014a0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ConfigureVirtualRouterElementCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ConfigureVirtualRouterElementCmd.java @@ -54,7 +54,7 @@ public class ConfigureVirtualRouterElementCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = VirtualRouterProviderResponse.class, required = true, - description = "the ID of the virtual router provider") + description = "The ID of the virtual router provider") private Long id; @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, required = true, description = "Enabled/Disabled the service provider") @@ -100,7 +100,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "configuring virtual router provider: " + id; + return "Configuring virtual router with provider that has ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -115,7 +115,7 @@ public Long getApiResourceId() { @Override public void execute() throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { - CallContext.current().setEventDetails("Virtual router element: " + id); + CallContext.current().setEventDetails("Virtual router element ID: " + getResourceUuid(ApiConstants.ID)); VirtualRouterProvider result = _service.get(0).configure(this); if (result != null) { VirtualRouterProviderResponse routerResponse = _responseGenerator.createVirtualRouterProviderResponse(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/CreateVirtualRouterElementCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/CreateVirtualRouterElementCmd.java index e85531c83c4d..094e87ae0058 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/CreateVirtualRouterElementCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/CreateVirtualRouterElementCmd.java @@ -54,7 +54,7 @@ public class CreateVirtualRouterElementCmd extends BaseAsyncCreateCmd { type = CommandType.UUID, entityType = ProviderResponse.class, required = true, - description = "the network service provider ID of the virtual router element") + description = "The network service provider ID of the virtual router element") private Long nspId; @Parameter(name = ApiConstants.PROVIDER_TYPE, @@ -98,7 +98,7 @@ public long getEntityOwnerId() { @Override public void execute() { - CallContext.current().setEventDetails("Virtual router element Id: " + getEntityUuid()); + CallContext.current().setEventDetails("Virtual router element ID: " + getEntityUuid()); VirtualRouterProvider result = _service.get(0).getCreatedElement(getEntityId()); if (result != null) { VirtualRouterProviderResponse response = _responseGenerator.createVirtualRouterProviderResponse(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/DestroyRouterCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/DestroyRouterCmd.java index 39ccee47fbeb..d8355cadb0a0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/DestroyRouterCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/DestroyRouterCmd.java @@ -42,7 +42,7 @@ public class DestroyRouterCmd extends BaseAsyncCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DomainRouterResponse.class, required = true, description = "the ID of the router") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DomainRouterResponse.class, required = true, description = "The ID of the router") private Long id; // /////////////////////////////////////////////////// @@ -74,7 +74,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "destroying router: " + this._uuidMgr.getUuid(VirtualMachine.class,getId()); + return "Destroying virtual router with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -90,7 +90,7 @@ public Long getApiResourceId() { @Override public void execute() throws ConcurrentOperationException, ResourceUnavailableException { CallContext ctx = CallContext.current(); - ctx.setEventDetails("Router Id: " + this._uuidMgr.getUuid(VirtualMachine.class,getId())); + ctx.setEventDetails("Router ID: " + getResourceUuid(ApiConstants.ID)); VirtualRouter result = _routerService.destroyRouter(getId(), ctx.getCallingAccount(), ctx.getCallingUserId()); if (result != null) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/GetRouterHealthCheckResultsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/GetRouterHealthCheckResultsCmd.java index 4bef26e05550..a302ebee1046 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/GetRouterHealthCheckResultsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/GetRouterHealthCheckResultsCmd.java @@ -52,10 +52,10 @@ public class GetRouterHealthCheckResultsCmd extends BaseCmd { ///////////////////////////////////////////////////// @Parameter(name = ApiConstants.ROUTER_ID, type = CommandType.UUID, entityType = DomainRouterResponse.class, - required = true, description = "the ID of the router") + required = true, description = "The ID of the router") private Long routerId; - @Parameter(name = ApiConstants.PERFORM_FRESH_CHECKS, type = CommandType.BOOLEAN, description = "if true is passed for this parameter, " + + @Parameter(name = ApiConstants.PERFORM_FRESH_CHECKS, type = CommandType.BOOLEAN, description = "If true is passed for this parameter, " + "health checks are performed on the fly. Else last performed checks data is fetched") private Boolean performFreshChecks; @@ -87,7 +87,7 @@ public long getEntityOwnerId() { @Override public void execute() throws ResourceUnavailableException, InvalidParameterValueException, ServerApiException { - CallContext.current().setEventDetails("Router Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getRouterId())); + CallContext.current().setEventDetails("Router ID: " + getResourceUuid(ApiConstants.ROUTER_ID)); VirtualRouter router = _routerService.findRouter(getRouterId()); if (router == null || router.getRole() != VirtualRouter.Role.VIRTUAL_ROUTER) { throw new InvalidParameterValueException("Can't find router by routerId"); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ListOvsElementsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ListOvsElementsCmd.java index a267aa526691..5ed3ae0e208e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ListOvsElementsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ListOvsElementsCmd.java @@ -46,13 +46,13 @@ public class ListOvsElementsCmd extends BaseListCmd { // /////////////////////////////////////////////////// // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = OvsProviderResponse.class, description = "list ovs elements by id") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = OvsProviderResponse.class, description = "List ovs elements by id") private Long id; - @Parameter(name = ApiConstants.NSP_ID, type = CommandType.UUID, entityType = ProviderResponse.class, description = "list ovs elements by network service provider id") + @Parameter(name = ApiConstants.NSP_ID, type = CommandType.UUID, entityType = ProviderResponse.class, description = "List ovs elements by network service provider id") private Long nspId; - @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, description = "list network offerings by enabled state") + @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, description = "List network offerings by enabled state") private Boolean enabled; // /////////////////////////////////////////////////// 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 9e34c05ce216..92ebd323169c 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 @@ -46,45 +46,45 @@ public class ListRoutersCmd extends BaseListProjectAndAccountResourcesCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "the host ID of the router") + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "The host ID of the router") private Long hostId; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, description = "the ID of the disk router") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, description = "The ID of the disk router") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the router") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the router") private String routerName; - @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "the Pod ID of the router") + @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "The Pod ID of the router") private Long podId; - @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "the state of the router") + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "The state of the router") private String state; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the Zone ID of the router") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The Zone ID of the router") private Long zoneId; - @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "the cluster ID of the router") + @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "The cluster ID of the router") private Long clusterId; - @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "list by network id") + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "List by network id") private Long networkId; @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, description = "List networks by VPC") private Long vpcId; - @Parameter(name = ApiConstants.FOR_VPC, type = CommandType.BOOLEAN, description = "if true is passed for this parameter, list only VPC routers") + @Parameter(name = ApiConstants.FOR_VPC, type = CommandType.BOOLEAN, description = "If true is passed for this parameter, list only VPC routers") private Boolean forVpc; - @Parameter(name = ApiConstants.VERSION, type = CommandType.STRING, description = "list virtual router elements by version") + @Parameter(name = ApiConstants.VERSION, type = CommandType.STRING, description = "List virtual router elements by version") private String version; @Parameter(name = ApiConstants.HEALTHCHECK_FAILED, type = CommandType.BOOLEAN, since = "4.16", - description = "if this parameter is passed, list only routers by health check results") + description = "If this parameter is passed, list only routers by health check results") private Boolean healthCheckFailed; @Parameter(name = ApiConstants.FETCH_ROUTER_HEALTH_CHECK_RESULTS, type = CommandType.BOOLEAN, since = "4.14", - description = "if true is passed for this parameter, also fetch last executed health check results for the router. Default is false") + 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, diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ListVirtualRouterElementsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ListVirtualRouterElementsCmd.java index 424b8c29d041..6b175b10ebd9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ListVirtualRouterElementsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ListVirtualRouterElementsCmd.java @@ -49,16 +49,16 @@ public class ListVirtualRouterElementsCmd extends BaseListCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VirtualRouterProviderResponse.class, description = "list virtual router elements by id") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VirtualRouterProviderResponse.class, description = "List virtual router elements by id") private Long id; @Parameter(name = ApiConstants.NSP_ID, type = CommandType.UUID, entityType = ProviderResponse.class, - description = "list virtual router elements by network service provider id") + description = "List virtual router elements by network service provider id") private Long nspId; - @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, description = "list network offerings by enabled state") + @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, description = "List network offerings by enabled state") private Boolean enabled; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/RebootRouterCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/RebootRouterCmd.java index 1d97dd803098..d90208e36d34 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/RebootRouterCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/RebootRouterCmd.java @@ -43,7 +43,7 @@ public class RebootRouterCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DomainRouterResponse.class, required = true, description = "the ID of the router") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DomainRouterResponse.class, required = true, description = "The ID of the router") private Long id; @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force reboot the router (Router is force Stopped and then Started)", since = "4.16.0") @@ -78,7 +78,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "rebooting router: " + this._uuidMgr.getUuid(VirtualMachine.class,getId()); + return "Rebooting router with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -97,7 +97,7 @@ public boolean isForced() { @Override public void execute() throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { - CallContext.current().setEventDetails("Router Id: " + this._uuidMgr.getUuid(VirtualMachine.class,getId())); + CallContext.current().setEventDetails("Router ID: " + getResourceUuid(ApiConstants.ID)); VirtualRouter result = _routerService.rebootRouter(getId(), true, isForced()); if (result != null) { DomainRouterResponse response = _responseGenerator.createDomainRouterResponse(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/StartRouterCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/StartRouterCmd.java index 24ab78810374..263f53ca2e48 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/StartRouterCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/StartRouterCmd.java @@ -45,7 +45,7 @@ public class StartRouterCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DomainRouterResponse.class, required = true, description = "the ID of the router") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DomainRouterResponse.class, required = true, description = "The ID of the router") private Long id; ///////////////////////////////////////////////////// @@ -81,7 +81,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "starting router: " + this._uuidMgr.getUuid(VirtualMachine.class, getId()); + return "Starting virtual router with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -96,7 +96,7 @@ public Long getApiResourceId() { @Override public void execute() throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { - CallContext.current().setEventDetails("Router Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getId())); + CallContext.current().setEventDetails("Router Id: " + getResourceUuid(ApiConstants.ID)); VirtualRouter result = null; VirtualRouter router = _routerService.findRouter(getId()); if (router == null || router.getRole() != Role.VIRTUAL_ROUTER) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/StopRouterCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/StopRouterCmd.java index 971086a57cff..838762740281 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/StopRouterCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/StopRouterCmd.java @@ -44,10 +44,10 @@ public class StopRouterCmd extends BaseAsyncCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DomainRouterResponse.class, required = true, description = "the ID of the router") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DomainRouterResponse.class, required = true, description = "The ID of the router") private Long id; - @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force stop the VM (vm is marked as Stopped even when command fails to be send to the backend, otherwise a force poweroff is attempted). To be used if the caller knows the VM is stopped and should be marked as such.") + @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force stop the Instance (Instance is marked as Stopped even when command fails to be send to the backend, otherwise a force poweroff is attempted). To be used if the caller knows the Instance is stopped and should be marked as such.") private Boolean forced; // /////////////////////////////////////////////////// @@ -79,7 +79,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Stopping router: " + this._uuidMgr.getUuid(VirtualMachine.class, getId()); + return "Stopping virtual router with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -98,7 +98,7 @@ public boolean isForced() { @Override public void execute() throws ConcurrentOperationException, ResourceUnavailableException { - CallContext.current().setEventDetails("Router Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getId())); + CallContext.current().setEventDetails("Router ID: " + getResourceUuid(ApiConstants.ID)); VirtualRouter result = null; VirtualRouter router = _routerService.findRouter(getId()); if (router == null || router.getRole() != Role.VIRTUAL_ROUTER) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/UpgradeRouterCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/UpgradeRouterCmd.java index 3265a089d672..1428ecbaeca2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/UpgradeRouterCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/UpgradeRouterCmd.java @@ -45,7 +45,7 @@ public class UpgradeRouterCmd extends BaseCmd { type = CommandType.UUID, entityType = ServiceOfferingResponse.class, required = true, - description = "the service offering ID to apply to the domain router") + description = "The service offering ID to apply to the domain router") private Long serviceOfferingId; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/UpgradeRouterTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/UpgradeRouterTemplateCmd.java index 74464cab3150..6fdfa7d3642d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/UpgradeRouterTemplateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/UpgradeRouterTemplateCmd.java @@ -39,7 +39,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.user.Account; -@APICommand(name = "upgradeRouterTemplate", description = "Upgrades router to use newer template", responseObject = BaseResponse.class, +@APICommand(name = "upgradeRouterTemplate", description = "Upgrades router to use newer Template", responseObject = BaseResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class UpgradeRouterTemplateCmd extends org.apache.cloudstack.api.BaseCmd { @@ -47,29 +47,29 @@ public class UpgradeRouterTemplateCmd extends org.apache.cloudstack.api.BaseCmd //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DomainRouterResponse.class, description = "upgrades router with the specified Id") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DomainRouterResponse.class, description = "Upgrades router with the specified Id") private Long id; @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, - description = "upgrades all routers within the specified cluster") + description = "Upgrades all routers within the specified cluster") private Long clusterId; - @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "upgrades all routers within the specified pod") + @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "Upgrades all routers within the specified Pod") private Long podId; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "upgrades all routers within the specified zone") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "Upgrades all routers within the specified zone") private Long zoneId; @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, - description="upgrades all routers owned by the specified account") + description = "Upgrades all routers owned by the specified account") private String account; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "upgrades all routers owned by the specified domain") + description = "Upgrades all routers owned by the specified domain") private Long domainId; ///////////////////////////////////////////////////// @@ -119,14 +119,14 @@ public Long getInstanceId() { @Override public void execute() throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { - CallContext.current().setEventDetails("Upgrading router template"); + CallContext.current().setEventDetails("Upgrading router with with ID: " + getResourceUuid(ApiConstants.ID) + " template"); List result = _routerService.upgradeRouterTemplate(this); if (result != null) { ListResponse response = _responseGenerator.createUpgradeRouterTemplateResponse(result); response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to upgrade router template"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to upgrade router Template"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/AddImageStoreCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/AddImageStoreCmd.java index 7c8f0e21afbb..72e2e96fe57b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/AddImageStoreCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/AddImageStoreCmd.java @@ -43,21 +43,21 @@ public class AddImageStoreCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name for the image store") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name for the image store") private String name; - @Parameter(name = ApiConstants.URL, type = CommandType.STRING, length = 2048, description = "the URL for the image store") + @Parameter(name = ApiConstants.URL, type = CommandType.STRING, length = 2048, description = "The URL for the image store") private String url; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the Zone ID for the image store") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The Zone ID for the image store") private Long zoneId; - @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, required = true, description = "the image store provider name") + @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, required = true, description = "The image store provider name") private String providerName; @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, - description = "the details for the image store. Example: details[0].key=accesskey&details[0].value=s389ddssaa&details[1].key=secretkey&details[1].value=8dshfsss") + description = "The details for the image store. Example: details[0].key=accesskey&details[0].value=s389ddssaa&details[1].key=secretkey&details[1].value=8dshfsss") private Map details; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/AddImageStoreS3CMD.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/AddImageStoreS3CMD.java index 2fe3c7cd106a..75fcf125eb10 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/AddImageStoreS3CMD.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/AddImageStoreS3CMD.java @@ -27,7 +27,7 @@ import static org.apache.cloudstack.api.ApiConstants.S3_HTTPS_FLAG; import static org.apache.cloudstack.api.ApiConstants.S3_MAX_ERROR_RETRY; import static org.apache.cloudstack.api.ApiConstants.S3_SIGNER; -import static org.apache.cloudstack.api.ApiConstants.S3_SECRET_KEY; +import static org.apache.cloudstack.api.ApiConstants.SECRET_KEY; import static org.apache.cloudstack.api.ApiConstants.S3_SOCKET_TIMEOUT; import static org.apache.cloudstack.api.ApiConstants.S3_USE_TCP_KEEPALIVE; import static org.apache.cloudstack.api.BaseCmd.CommandType.BOOLEAN; @@ -64,7 +64,7 @@ public final class AddImageStoreS3CMD extends BaseCmd implements ClientOptions { @Parameter(name = S3_ACCESS_KEY, type = STRING, required = true, description = "S3 access key") private String accessKey; - @Parameter(name = S3_SECRET_KEY, type = STRING, required = true, description = "S3 secret key") + @Parameter(name = SECRET_KEY, type = STRING, required = true, description = "S3 secret key") private String secretKey; @Parameter(name = S3_END_POINT, type = STRING, required = true, description = "S3 endpoint") @@ -101,7 +101,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE Map dm = new HashMap(); dm.put(ApiConstants.S3_ACCESS_KEY, getAccessKey()); - dm.put(ApiConstants.S3_SECRET_KEY, getSecretKey()); + dm.put(ApiConstants.SECRET_KEY, getSecretKey()); dm.put(ApiConstants.S3_END_POINT, getEndPoint()); dm.put(ApiConstants.S3_BUCKET_NAME, getBucketName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/CancelPrimaryStorageMaintenanceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/CancelPrimaryStorageMaintenanceCmd.java index 7e925f286d09..7b69d25caef4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/CancelPrimaryStorageMaintenanceCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/CancelPrimaryStorageMaintenanceCmd.java @@ -42,7 +42,7 @@ public class CancelPrimaryStorageMaintenanceCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, required = true, description = "the primary storage ID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, required = true, description = "The primary storage ID") private Long id; ///////////////////////////////////////////////////// @@ -93,7 +93,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "canceling maintenance for primary storage pool: " + getId(); + return "Canceling maintenance mode for primary storage pool with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ChangeStoragePoolScopeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ChangeStoragePoolScopeCmd.java index d3b6a0746106..3bb16dfea5a4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ChangeStoragePoolScopeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ChangeStoragePoolScopeCmd.java @@ -28,7 +28,6 @@ import org.apache.cloudstack.context.CallContext; import com.cloud.event.EventTypes; -import com.cloud.storage.StoragePool; @APICommand(name = "changeStoragePoolScope", description = "Changes the scope of a storage pool when the pool is in Disabled state." + "This feature is officially tested and supported for Hypervisors: KVM and VMware, Protocols: NFS and Ceph, and Storage Provider: DefaultPrimary. " + @@ -61,15 +60,7 @@ public String getEventType() { @Override public String getEventDescription() { - String description = "Change storage pool scope. Storage pool Id: "; - StoragePool pool = _entityMgr.findById(StoragePool.class, getId()); - if (pool != null) { - description += pool.getUuid(); - } else { - description += getId(); - } - description += " to " + getScope(); - return description; + return "Changing storage pool with ID: " + getResourceUuid(ApiConstants.ID) + " to scope " + scope; } @Override 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 index bfa2589921f7..c5459adfd2d0 100644 --- 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 @@ -130,6 +130,6 @@ public String getEventType() { @Override public String getEventDescription() { - return "configuring storage access groups"; + return "Configuring storage access groups"; } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/CreateSecondaryStagingStoreCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/CreateSecondaryStagingStoreCmd.java index 5776eb6464cf..e197e9f94f11 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/CreateSecondaryStagingStoreCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/CreateSecondaryStagingStoreCmd.java @@ -34,7 +34,7 @@ import com.cloud.storage.ImageStore; import com.cloud.user.Account; -@APICommand(name = "createSecondaryStagingStore", description = "create secondary staging store.", responseObject = ImageStoreResponse.class, +@APICommand(name = "createSecondaryStagingStore", description = "Create secondary staging store.", responseObject = ImageStoreResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateSecondaryStagingStoreCmd extends BaseCmd { @@ -42,19 +42,19 @@ public class CreateSecondaryStagingStoreCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, length = 2048, description = "the URL for the staging store") + @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, length = 2048, description = "The URL for the staging store") private String url; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the Zone ID for the staging store") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The Zone ID for the staging store") private Long zoneId; - @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "the details for the staging store") + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "The details for the staging store") private Map> details; - @Parameter(name = ApiConstants.SCOPE, type = CommandType.STRING, required = false, description = "the scope of the staging store: zone only for now") + @Parameter(name = ApiConstants.SCOPE, type = CommandType.STRING, required = false, description = "The scope of the staging store: zone only for now") private String scope; - @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, required = false, description = "the staging store provider name") + @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, required = false, description = "The staging store provider name") private String providerName; ///////////////////////////////////////////////////// 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 cbe4b8c06b3f..2aef856f58f3 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 @@ -46,50 +46,50 @@ public class CreateStoragePoolCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "the cluster ID for the storage pool") + @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "The cluster ID for the storage pool") private Long clusterId; - @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "the details for the storage pool") + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "The details for the storage pool") private Map details; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name for the storage pool") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "The name for the storage pool") private String storagePoolName; - @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "the Pod ID for the storage pool") + @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "The Pod ID for the storage pool") private Long podId; - @Parameter(name = ApiConstants.TAGS, type = CommandType.STRING, description = "the tags for the storage pool") + @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") + @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, description = "The URL of the storage pool") private String url; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "the Zone ID for the storage pool") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "The Zone ID for the storage pool") private Long zoneId; - @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, required = false, description = "the storage provider name") + @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, required = false, description = "The storage provider name") private String storageProviderName; - @Parameter(name = ApiConstants.SCOPE, type = CommandType.STRING, required = false, description = "the scope of the storage: cluster or zone") + @Parameter(name = ApiConstants.SCOPE, type = CommandType.STRING, required = false, description = "The scope of the storage: cluster or zone") private String scope; - @Parameter(name = ApiConstants.MANAGED, type = CommandType.BOOLEAN, required = false, description = "whether the storage should be managed by CloudStack") + @Parameter(name = ApiConstants.MANAGED, type = CommandType.BOOLEAN, required = false, description = "Whether the storage should be managed by CloudStack") private Boolean managed; @Parameter(name = ApiConstants.CAPACITY_IOPS, type = CommandType.LONG, required = false, description = "IOPS CloudStack can provision from this storage pool") private Long capacityIops; - @Parameter(name = ApiConstants.CAPACITY_BYTES, type = CommandType.LONG, required = false, description = "bytes CloudStack can provision from this storage pool") + @Parameter(name = ApiConstants.CAPACITY_BYTES, type = CommandType.LONG, required = false, description = "Bytes CloudStack can provision from this storage pool") private Long capacityBytes; @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, required = false, - description = "hypervisor type of the hosts in zone that will be attached to this storage pool. KVM, VMware supported as of now.") + description = "Hypervisor type of the hosts in zone that will be attached to this storage pool. KVM, VMware supported as of now.") private String hypervisor; @Parameter(name = ApiConstants.IS_TAG_A_RULE, type = CommandType.BOOLEAN, description = ApiConstants.PARAMETER_DESCRIPTION_IS_TAG_A_RULE) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/DeleteSecondaryStagingStoreCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/DeleteSecondaryStagingStoreCmd.java index a0c2731ccdaf..68d9f37df3f0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/DeleteSecondaryStagingStoreCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/DeleteSecondaryStagingStoreCmd.java @@ -37,7 +37,7 @@ public class DeleteSecondaryStagingStoreCmd extends BaseCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ImageStoreResponse.class, required = true, description = "the staging store ID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ImageStoreResponse.class, required = true, description = "The staging store ID") private Long id; // /////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/DownloadImageStoreObjectCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/DownloadImageStoreObjectCmd.java index 92019e70eca2..1d927ac5cbd6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/DownloadImageStoreObjectCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/DownloadImageStoreObjectCmd.java @@ -94,6 +94,6 @@ public String getEventType() { @Override public String getEventDescription() { - return "Downloading object at path " + getPath() + " on image store " + getStoreId(); + return "Downloading object at path " + getPath() + " on image store " + getResourceUuid(ApiConstants.ID); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/FindStoragePoolsForMigrationCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/FindStoragePoolsForMigrationCmd.java index a45f72766dc6..7f5e864ed910 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/FindStoragePoolsForMigrationCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/FindStoragePoolsForMigrationCmd.java @@ -43,7 +43,7 @@ public class FindStoragePoolsForMigrationCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VolumeResponse.class, required = true, description = "the ID of the volume") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VolumeResponse.class, required = true, description = "The ID of the volume") private Long id; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListImageStoresCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListImageStoresCmd.java index 5270569de44e..62d460c354c0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListImageStoresCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListImageStoresCmd.java @@ -33,22 +33,22 @@ public class ListImageStoresCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the image store") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the image store") private String storeName; - @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "the image store protocol") + @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "The image store protocol") private String protocol; - @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, description = "the image store provider") + @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, description = "The image store provider") private String provider; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the Zone ID for the image store") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The Zone ID for the image store") private Long zoneId; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ImageStoreResponse.class, description = "the ID of the storage pool") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ImageStoreResponse.class, description = "The ID of the storage pool") private Long id; - @Parameter(name = ApiConstants.READ_ONLY, type = CommandType.BOOLEAN, entityType = ImageStoreResponse.class, description = "read-only status of the image store", since = "4.15.0") + @Parameter(name = ApiConstants.READ_ONLY, type = CommandType.BOOLEAN, entityType = ImageStoreResponse.class, description = "Read-only status of the image store", since = "4.15.0") private Boolean readonly; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListSecondaryStagingStoresCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListSecondaryStagingStoresCmd.java index 0cad16a247fd..31cd3519c5bb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListSecondaryStagingStoresCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListSecondaryStagingStoresCmd.java @@ -35,19 +35,19 @@ public class ListSecondaryStagingStoresCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the staging store") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the staging store") private String storeName; - @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "the staging store protocol") + @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "The staging store protocol") private String protocol; - @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, description = "the staging store provider") + @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, description = "The staging store provider") private String provider; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the Zone ID for the staging store") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The Zone ID for the staging store") private Long zoneId; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ImageStoreResponse.class, description = "the ID of the staging store") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ImageStoreResponse.class, description = "The ID of the staging store") private Long id; ///////////////////////////////////////////////////// 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 0f2c9f3416e7..236a3769b64c 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,32 +41,32 @@ public class ListStoragePoolsCmd extends BaseListCmd { @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, - description = "list storage pools belonging 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") + @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, description = "The IP address for the storage pool") private String ipAddress; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the storage pool") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the storage pool") private String storagePoolName; - @Parameter(name = ApiConstants.PATH, type = CommandType.STRING, description = "the storage pool path") + @Parameter(name = ApiConstants.PATH, type = CommandType.STRING, description = "The storage pool path") private String path; - @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "the Pod ID for the storage pool") + @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "The Pod ID for the storage pool") private Long podId; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the Zone ID for the storage pool") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The Zone ID for the storage pool") private Long zoneId; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, description = "the ID of the storage pool") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, description = "The ID of the storage pool") private Long id; - @Parameter(name = ApiConstants.SCOPE, type = CommandType.STRING, entityType = StoragePoolResponse.class, description = "the scope of the storage pool") + @Parameter(name = ApiConstants.SCOPE, type = CommandType.STRING, entityType = StoragePoolResponse.class, description = "The scope of the storage pool") private String scope; - @Parameter(name = ApiConstants.STATUS, type = CommandType.STRING, description = "the status of the storage pool") + @Parameter(name = ApiConstants.STATUS, type = CommandType.STRING, description = "The status of the storage pool") private String status; @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "host ID of the storage pools") diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStorageProvidersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStorageProvidersCmd.java index efe7a23b5cb4..f7d4c1078359 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStorageProvidersCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStorageProvidersCmd.java @@ -40,7 +40,7 @@ requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListStorageProvidersCmd extends BaseListCmd { - @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "the type of storage provider: either primary or image", required = true) + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "The type of storage provider: either primary or image", required = true) private String type; public String getType() { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/MigrateSecondaryStorageDataCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/MigrateSecondaryStorageDataCmd.java index 8f5a7aced3fe..edd9c25fbc10 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/MigrateSecondaryStorageDataCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/MigrateSecondaryStorageDataCmd.java @@ -32,7 +32,7 @@ import com.cloud.event.EventTypes; @APICommand(name = "migrateSecondaryStorageData", - description = "migrates data objects from one secondary storage to destination image store(s)", + description = "Migrates data objects from one secondary storage to destination image store(s)", responseObject = MigrationResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, @@ -49,7 +49,7 @@ public class MigrateSecondaryStorageDataCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.SRC_POOL, type = CommandType.UUID, entityType = ImageStoreResponse.class, - description = "id of the image store from where the data is to be migrated", + description = "ID of the image store from where the data is to be migrated", required = true) private Long id; @@ -57,7 +57,7 @@ public class MigrateSecondaryStorageDataCmd extends BaseAsyncCmd { type = CommandType.LIST, collectionType = CommandType.UUID, entityType = ImageStoreResponse.class, - description = "id(s) of the destination secondary storage pool(s) to which the templates are to be migrated", + description = "ID(s) of the destination secondary storage pool(s) to which the Templates are to be migrated", required = true) private List migrateTo; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/PreparePrimaryStorageForMaintenanceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/PreparePrimaryStorageForMaintenanceCmd.java index 818b3a5bbeab..9f0efe7f7a1b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/PreparePrimaryStorageForMaintenanceCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/PreparePrimaryStorageForMaintenanceCmd.java @@ -93,7 +93,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "preparing storage pool: " + getId() + " for maintenance"; + return "Preparing storage pool with ID: " + getResourceUuid(ApiConstants.ID) + " for maintenance"; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/SyncStoragePoolCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/SyncStoragePoolCmd.java index 9f81f2f6c86c..684243c08299 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/SyncStoragePoolCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/SyncStoragePoolCmd.java @@ -67,7 +67,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Attempting to synchronise storage pool with management server"; + return "Attempting to synchronise storage pool with ID:" + getResourceUuid(ApiConstants.ID) + " with management server"; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateCloudToUseObjectStoreCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateCloudToUseObjectStoreCmd.java index 5ac34f27bada..022bbb9a9057 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateCloudToUseObjectStoreCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateCloudToUseObjectStoreCmd.java @@ -42,17 +42,17 @@ public class UpdateCloudToUseObjectStoreCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name=ApiConstants.NAME, type=CommandType.STRING, description="the name for the image store") + @Parameter(name=ApiConstants.NAME, type=CommandType.STRING, description = "The name for the image store") private String name; - @Parameter(name=ApiConstants.URL, type=CommandType.STRING, description="the URL for the image store") + @Parameter(name=ApiConstants.URL, type=CommandType.STRING, description = "The URL for the image store") private String url; @Parameter(name=ApiConstants.PROVIDER, type=CommandType.STRING, - required=true, description="the image store provider name") + required=true, description = "The image store provider name") private String providerName; - @Parameter(name=ApiConstants.DETAILS, type=CommandType.MAP, description="the details for the image store. Example: details[0].key=accesskey&details[0].value=s389ddssaa&details[1].key=secretkey&details[1].value=8dshfsss") + @Parameter(name=ApiConstants.DETAILS, type=CommandType.MAP, description = "The details for the image store. Example: details[0].key=accesskey&details[0].value=s389ddssaa&details[1].key=secretkey&details[1].value=8dshfsss") private Map details; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateStoragePoolCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateStoragePoolCmd.java index f2d7bbeb189e..4b0a6ba00b28 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateStoragePoolCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateStoragePoolCmd.java @@ -44,22 +44,22 @@ public class UpdateStoragePoolCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, required = true, description = "the Id of the storage pool") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, required = true, description = "The Id of the storage pool") private Long id; @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, entityType = StoragePoolResponse.class, description = "Change the name of the storage pool", since = "4.15") private String name; - @Parameter(name = ApiConstants.TAGS, type = CommandType.LIST, collectionType = CommandType.STRING, description = "comma-separated list of tags for the storage pool") + @Parameter(name = ApiConstants.TAGS, type = CommandType.LIST, collectionType = CommandType.STRING, description = "Comma-separated list of tags for the storage pool") private List tags; @Parameter(name = ApiConstants.CAPACITY_IOPS, type = CommandType.LONG, required = false, description = "IOPS CloudStack can provision from this storage pool") private Long capacityIops; - @Parameter(name = ApiConstants.CAPACITY_BYTES, type = CommandType.LONG, required = false, description = "bytes CloudStack can provision from this storage pool") + @Parameter(name = ApiConstants.CAPACITY_BYTES, type = CommandType.LONG, required = false, description = "Bytes CloudStack can provision from this storage pool") private Long capacityBytes; - @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, required = false, description = "false to disable the pool for allocation of new volumes, true to" + + @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, required = false, description = "False to disable the pool for allocation of new volumes, true to" + " enable it back.") private Boolean enabled; @@ -153,6 +153,8 @@ public void execute() { if (ObjectUtils.anyNotNull(name, capacityIops, capacityBytes, url, isTagARule, tags) || MapUtils.isNotEmpty(details)) { result = _storageService.updateStoragePool(this); + } else { + result = _storageService.getStoragePool(getId()); } if (enabled != null) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/swift/AddSwiftCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/swift/AddSwiftCmd.java index bd72f3213de1..cc0c77348a90 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/swift/AddSwiftCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/swift/AddSwiftCmd.java @@ -40,13 +40,13 @@ public class AddSwiftCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, description = "the URL for swift") + @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, description = "The URL for swift") private String url; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "the account for swift") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "The account for swift") private String account; - @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, description = "the username for swift") + @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, description = "The username for swift") private String username; @Parameter(name = ApiConstants.KEY, type = CommandType.STRING, description = " key for the user for swift") diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/swift/ListSwiftsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/swift/ListSwiftsCmd.java index e21a23349bb5..6645cd60023c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/swift/ListSwiftsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/swift/ListSwiftsCmd.java @@ -35,7 +35,7 @@ public class ListSwiftsCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.LONG, description = "the id of the swift") + @Parameter(name = ApiConstants.ID, type = CommandType.LONG, description = "The id of the swift") private Long id; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/DestroySystemVmCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/DestroySystemVmCmd.java index 7e0faab2fb50..6b776d067784 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/DestroySystemVmCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/DestroySystemVmCmd.java @@ -33,7 +33,7 @@ import com.cloud.user.Account; import com.cloud.vm.VirtualMachine; -@APICommand(name = "destroySystemVm", responseObject = SystemVmResponse.class, description = "Destroys a system virtual machine.", entityType = {VirtualMachine.class}, +@APICommand(name = "destroySystemVm", responseObject = SystemVmResponse.class, description = "Destroys a System VM.", entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DestroySystemVmCmd extends BaseAsyncCmd { @@ -43,7 +43,7 @@ public class DestroySystemVmCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = SystemVmResponse.class, required = true, - description = "The ID of the system virtual machine") + description = "The ID of the System VM") private Long id; public Long getId() { @@ -76,7 +76,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "destroying system vm: " + this._uuidMgr.getUuid(VirtualMachine.class, getId()); + return "Destroying System VM with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -91,14 +91,14 @@ public Long getApiResourceId() { @Override public void execute() { - CallContext.current().setEventDetails("Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getId())); + CallContext.current().setEventDetails("System VM ID: " + getResourceUuid(ApiConstants.ID)); VirtualMachine instance = _mgr.destroySystemVM(this); if (instance != null) { SystemVmResponse response = _responseGenerator.createSystemVmResponse(instance); response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Fail to destroy system vm"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to destroy System VM"); } } } 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 13113e17ea4d..d339df850168 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 @@ -36,7 +36,7 @@ import com.cloud.utils.Pair; import com.cloud.vm.VirtualMachine; -@APICommand(name = "listSystemVms", description = "List system virtual machines.", responseObject = SystemVmResponse.class, entityType = {VirtualMachine.class}, +@APICommand(name = "listSystemVms", description = "List System VMs.", responseObject = SystemVmResponse.class, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListSystemVMsCmd extends BaseListCmd { @@ -45,33 +45,33 @@ public class ListSystemVMsCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "the host ID of the system VM") + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "The host ID of the system VM") private Long hostId; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SystemVmResponse.class, description = "the ID of the system VM") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SystemVmResponse.class, description = "The ID of the system VM") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the system VM") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the system VM") private String systemVmName; - @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "the Pod ID of the system VM") + @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "The Pod ID of the system VM") private Long podId; - @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "the state of the system VM") + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "The state of the system VM") private String state; @Parameter(name = ApiConstants.SYSTEM_VM_TYPE, type = CommandType.STRING, - description = "the system VM type. Possible types are \"consoleproxy\" and \"secondarystoragevm\".") + description = "The system VM type. Possible types are \"consoleproxy\" and \"secondarystoragevm\".") private String systemVmType; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the Zone ID of the system VM") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The Zone ID of the system VM") private Long zoneId; @Parameter(name = ApiConstants.STORAGE_ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, - description = "the storage ID where vm's volumes belong to", + description = "The storage ID where Instance's volumes belong to", since = "3.0.1") private Long storageId; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/MigrateSystemVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/MigrateSystemVMCmd.java index ccc6093aa834..feeb3f44553d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/MigrateSystemVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/MigrateSystemVMCmd.java @@ -43,7 +43,7 @@ import com.cloud.user.Account; import com.cloud.vm.VirtualMachine; -@APICommand(name = "migrateSystemVm", description = "Attempts Migration of a system virtual machine to the host specified.", responseObject = SystemVmResponse.class, entityType = {VirtualMachine.class}, +@APICommand(name = "migrateSystemVm", description = "Attempts Migration of a System VM to the host specified.", responseObject = SystemVmResponse.class, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class MigrateSystemVMCmd extends BaseAsyncCmd { @@ -55,7 +55,7 @@ public class MigrateSystemVMCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, - description = "destination Host ID to migrate VM to") + description = "Destination Host ID to migrate Instance to") private Long hostId; @ACL(accessType = AccessType.OperateEntry) @@ -63,14 +63,14 @@ public class MigrateSystemVMCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = SystemVmResponse.class, required = true, - description = "the ID of the virtual machine") + description = "The ID of the Instance") private Long virtualMachineId; @Parameter(name = ApiConstants.STORAGE_ID, since = "4.16.0", type = CommandType.UUID, entityType = StoragePoolResponse.class, - description = "Destination storage pool ID to migrate VM volumes to. Required for migrating the root disk volume") + description = "Destination storage pool ID to migrate Instance volumes to. Required for migrating the root disk volume") private Long storageId; @Parameter(name = ApiConstants.AUTO_SELECT, @@ -120,7 +120,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Attempting to migrate VM Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getVirtualMachineId()) + " to host Id: " + this._uuidMgr.getUuid(Host.class, getHostId()); + return "Attempting to migrate System VM with ID: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID) + " to host with ID: " + getResourceUuid(ApiConstants.HOST_ID); } @Override @@ -136,24 +136,24 @@ public void execute() { // OfflineMigration performed when this parameter is specified StoragePool destStoragePool = _storageService.getStoragePool(getStorageId()); if (destStoragePool == null) { - throw new InvalidParameterValueException("Unable to find the storage pool to migrate the VM"); + throw new InvalidParameterValueException("Unable to find the storage pool to migrate the Instance"); } - CallContext.current().setEventDetails("VM Id: " + getVirtualMachineId() + " to storage pool Id: " + getStorageId()); + CallContext.current().setEventDetails("System VM ID: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID) + " to storage pool with ID: " + getResourceUuid(ApiConstants.STORAGE_ID)); migratedVm = _userVmService.vmStorageMigration(getVirtualMachineId(), destStoragePool); } else { Host destinationHost = null; if (getHostId() != null) { destinationHost =_resourceService.getHost(getHostId()); if (destinationHost == null) { - throw new InvalidParameterValueException("Unable to find the host to migrate the VM, host id=" + getHostId()); + throw new InvalidParameterValueException("Unable to find the host to migrate the Instance, host id=" + getHostId()); } if (destinationHost.getType() != Host.Type.Routing) { - throw new InvalidParameterValueException("The specified host(" + destinationHost.getName() + ") is not suitable to migrate the VM, please specify another one"); + throw new InvalidParameterValueException("The specified host(" + destinationHost.getName() + ") is not suitable to migrate the Instance, please specify another one"); } } else if (! isAutoSelect()) { throw new InvalidParameterValueException("Please specify a host or storage as destination, or pass 'autoselect=true' to automatically select a destination host which do not require storage migration"); } - CallContext.current().setEventDetails("VM Id: " + getVirtualMachineId() + " to host Id: " + getHostId()); + CallContext.current().setEventDetails("System VM ID: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID) + " to host with ID: " + getResourceUuid(ApiConstants.HOST_ID)); if (destinationHost == null) { migratedVm = _userVmService.migrateVirtualMachine(getVirtualMachineId(), null); } else { 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 eafee7424ffc..13f6167513b0 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 @@ -19,7 +19,6 @@ import com.cloud.event.EventTypes; import com.cloud.user.Account; import com.cloud.utils.Pair; -import com.cloud.vm.VirtualMachine; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -40,7 +39,7 @@ public class PatchSystemVMCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SystemVmResponse.class, - description = "patches systemVM - CPVM/SSVM with the specified ID") + description = "Patches System VM - CPVM/SSVM with the specified ID") private Long id; @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, @@ -72,7 +71,7 @@ public String getEventType() { @Override public String getEventDescription() { - return String.format("Attempting to live patch System VM with Id: %s ", this._uuidMgr.getUuid(VirtualMachine.class, getId())); + return String.format("Attempting to live patch System VM with ID: %s ", getResourceUuid(ApiConstants.ID)); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/RebootSystemVmCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/RebootSystemVmCmd.java index 30bd51184ac3..0a0ffe847adc 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/RebootSystemVmCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/RebootSystemVmCmd.java @@ -46,10 +46,10 @@ public class RebootSystemVmCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = SystemVmResponse.class, required = true, - description = "The ID of the system virtual machine") + description = "The ID of the System VM") private Long id; - @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force reboot the system VM (System VM is Stopped and then Started)", since = "4.16.0") + @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force reboot the System VM (System VM is Stopped and then Started)", since = "4.16.0") private Boolean forced; ///////////////////////////////////////////////////// @@ -86,7 +86,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "rebooting system vm: " + this._uuidMgr.getUuid(VirtualMachine.class, getId()); + return "Rebooting System VM with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -105,14 +105,14 @@ public boolean isForced() { @Override public void execute() { - CallContext.current().setEventDetails("Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getId())); + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.ID)); VirtualMachine result = _mgr.rebootSystemVM(this); if (result != null) { SystemVmResponse response = _responseGenerator.createSystemVmResponse(result); response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Fail to reboot system vm"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to reboot System Instance"); } } } 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 d822d4fc26ef..061a2ad2deed 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 @@ -55,10 +55,10 @@ public class ScaleSystemVMCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SystemVmResponse.class, required = true, description = "The ID of the system vm") private Long id; - @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, required = true, description = "the service offering ID to apply to the system vm") + @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, required = true, description = "The service offering ID to apply to the system vm") private Long serviceOfferingId; - @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "name value pairs of custom parameters for cpuspeed, memory and cpunumber. example details[i].name=value") + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "Name value pairs of custom parameters for cpuspeed, memory and cpunumber. example details[i].name=value") private Map details; ///////////////////////////////////////////////////// @@ -98,7 +98,7 @@ public long getEntityOwnerId() { @Override public void execute() { - CallContext.current().setEventDetails("SystemVm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getId())); + CallContext.current().setEventDetails("System VM ID: " + getResourceUuid(ApiConstants.ID)); ServiceOffering serviceOffering = _entityMgr.findById(ServiceOffering.class, serviceOfferingId); if (serviceOffering == null) { @@ -137,6 +137,6 @@ public String getEventType() { @Override public String getEventDescription() { - return "Upgrading system vm: " + this._uuidMgr.getUuid(VirtualMachine.class, getId()) + " to service offering: " + this._uuidMgr.getUuid(ServiceOffering.class, getServiceOfferingId()); + return "Upgrading System VM with ID: " + getResourceUuid(ApiConstants.ID) + " to service offering: " + getResourceUuid(ApiConstants.SERVICE_OFFERING_ID); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/StartSystemVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/StartSystemVMCmd.java index eac3d64ab59e..734b553c2dd5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/StartSystemVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/StartSystemVMCmd.java @@ -33,7 +33,7 @@ import com.cloud.user.Account; import com.cloud.vm.VirtualMachine; -@APICommand(name = "startSystemVm", responseObject = SystemVmResponse.class, description = "Starts a system virtual machine.", entityType = {VirtualMachine.class}, +@APICommand(name = "startSystemVm", responseObject = SystemVmResponse.class, description = "Starts a System VM.", entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class StartSystemVMCmd extends BaseAsyncCmd { @@ -46,7 +46,7 @@ public class StartSystemVMCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = SystemVmResponse.class, required = true, - description = "The ID of the system virtual machine") + description = "The ID of the System VM") private Long id; ///////////////////////////////////////////////////// @@ -87,7 +87,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "starting system vm: " + this._uuidMgr.getUuid(VirtualMachine.class, getId()); + return "Starting System VM with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -102,7 +102,7 @@ public Long getApiResourceId() { @Override public void execute() { - CallContext.current().setEventDetails("Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getId())); + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.ID)); VirtualMachine instance = _mgr.startSystemVM(getId()); if (instance != null) { SystemVmResponse response = _responseGenerator.createSystemVmResponse(instance); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/StopSystemVmCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/StopSystemVmCmd.java index 1d84382f5d22..bdcb5b407b39 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/StopSystemVmCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/StopSystemVmCmd.java @@ -49,10 +49,10 @@ public class StopSystemVmCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = SystemVmResponse.class, required = true, - description = "The ID of the system virtual machine") + description = "The ID of the System VM") private Long id; - @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force stop the VM (vm is marked as Stopped even when command fails to be send to the backend, otherwise a force poweroff is attempted). To be used if the caller knows the VM is stopped and should be marked as such.") + @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force stop the Instance (Instance is marked as Stopped even when command fails to be send to the backend, otherwise a force poweroff is attempted). To be used if the caller knows the Instance is stopped and should be marked as such.") private Boolean forced; ///////////////////////////////////////////////////// @@ -89,7 +89,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "stopping system vm: " + this._uuidMgr.getUuid(VirtualMachine.class, getId()); + return "Stopping System VM with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -108,7 +108,7 @@ public boolean isForced() { @Override public void execute() throws ResourceUnavailableException, ConcurrentOperationException { - CallContext.current().setEventDetails("Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getId())); + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.ID)); VirtualMachine result = _mgr.stopSystemVM(this); if (result != null) { SystemVmResponse response = _responseGenerator.createSystemVmResponse(result); 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 a42f00522496..a27c04374c32 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 @@ -46,13 +46,13 @@ public class UpgradeSystemVMCmd extends BaseCmd { ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SystemVmResponse.class, required = true, description = "The ID of the system vm") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SystemVmResponse.class, required = true, description = "The ID of the System VM") private Long id; - @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, required = true, description = "the service offering ID to apply to the system vm") + @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, required = true, description = "The service offering ID to apply to the System VM") private Long serviceOfferingId; - @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "name value pairs of custom parameters for cpuspeed, memory and cpunumber. example details[i].name=value") + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "Name value pairs of custom parameters for cpuspeed, memory and cpunumber. example details[i].name=value") private Map details; ///////////////////////////////////////////////////// @@ -87,7 +87,7 @@ public long getEntityOwnerId() { @Override public void execute() { - CallContext.current().setEventDetails("Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getId())); + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.ID)); ServiceOffering serviceOffering = _entityMgr.findById(ServiceOffering.class, serviceOfferingId); if (serviceOffering == null) { @@ -100,7 +100,7 @@ public void execute() { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Fail to reboot system vm"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to reboot System VM"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/template/CopyTemplateCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/template/CopyTemplateCmdByAdmin.java index b937e0b940cc..8e2854db308d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/template/CopyTemplateCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/template/CopyTemplateCmdByAdmin.java @@ -21,6 +21,6 @@ import org.apache.cloudstack.api.command.user.template.CopyTemplateCmd; import org.apache.cloudstack.api.response.TemplateResponse; -@APICommand(name = "copyTemplate", description = "Copies a template from one zone to another.", responseObject = TemplateResponse.class, responseView = ResponseView.Full, +@APICommand(name = "copyTemplate", description = "Copies a Template from one zone to another.", responseObject = TemplateResponse.class, responseView = ResponseView.Full, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CopyTemplateCmdByAdmin extends CopyTemplateCmd {} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/template/CreateTemplateCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/template/CreateTemplateCmdByAdmin.java index 12609493b637..18a17c861ad1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/template/CreateTemplateCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/template/CreateTemplateCmdByAdmin.java @@ -22,7 +22,7 @@ import org.apache.cloudstack.api.command.user.template.CreateTemplateCmd; import org.apache.cloudstack.api.response.TemplateResponse; -@APICommand(name = "createTemplate", responseObject = TemplateResponse.class, description = "Creates a template of a virtual machine. " + "The virtual machine must be in a STOPPED state. " - + "A template created from this command is automatically designated as a private template visible to the account that created it.", responseView = ResponseView.Full, +@APICommand(name = "createTemplate", responseObject = TemplateResponse.class, description = "Creates a Template of an Instance. " + "The Instance must be in a STOPPED state. " + + "A Template created from this command is automatically designated as a private Template visible to the account that created it.", responseView = ResponseView.Full, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateTemplateCmdByAdmin extends CreateTemplateCmd implements AdminCmd {} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/template/ListTemplatePermissionsCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/template/ListTemplatePermissionsCmdByAdmin.java index ae0e220b4952..6115ff98f38a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/template/ListTemplatePermissionsCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/template/ListTemplatePermissionsCmdByAdmin.java @@ -1,4 +1,4 @@ -// Licensedname = "listTemplatePermissions", to the Apache Software Foundation (ASF) under one +// 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 @@ -22,7 +22,7 @@ import org.apache.cloudstack.api.command.user.template.ListTemplatePermissionsCmd; import org.apache.cloudstack.api.response.TemplatePermissionsResponse; -@APICommand(name = "listTemplatePermissions", description = "List template visibility and all accounts that have permissions to view this template.", responseObject = TemplatePermissionsResponse.class, responseView = ResponseView.Full, +@APICommand(name = "listTemplatePermissions", description = "List Template visibility and all accounts that have permissions to view this Template.", responseObject = TemplatePermissionsResponse.class, responseView = ResponseView.Full, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListTemplatePermissionsCmdByAdmin extends ListTemplatePermissionsCmd implements AdminCmd {} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/template/ListTemplatesCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/template/ListTemplatesCmdByAdmin.java index 22eb0ff06599..54c0c8f702d5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/template/ListTemplatesCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/template/ListTemplatesCmdByAdmin.java @@ -28,7 +28,7 @@ import com.cloud.template.VirtualMachineTemplate; -@APICommand(name = "listTemplates", description = "List all public, private, and privileged templates.", +@APICommand(name = "listTemplates", description = "List all public, private, and privileged Templates.", responseObject = TemplateResponse.class, entityType = {VirtualMachineTemplate.class}, responseView = ResponseView.Full, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListTemplatesCmdByAdmin extends ListTemplatesCmd implements AdminCmd { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/template/PrepareTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/template/PrepareTemplateCmd.java index 9a59efb19f2a..136522d6bf80 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/template/PrepareTemplateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/template/PrepareTemplateCmd.java @@ -34,7 +34,7 @@ import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.Account; -@APICommand(name = "prepareTemplate", responseObject = TemplateResponse.class, description = "load template into primary storage", entityType = {VirtualMachineTemplate.class}, +@APICommand(name = "prepareTemplate", responseObject = TemplateResponse.class, description = "Load Template into primary storage", entityType = {VirtualMachineTemplate.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class PrepareTemplateCmd extends BaseCmd { @@ -47,7 +47,7 @@ public class PrepareTemplateCmd extends BaseCmd { type = CommandType.UUID, entityType = ZoneResponse.class, required = true, - description = "zone ID of the template to be prepared in primary storage(s).") + description = "Zone ID of the Template to be prepared in primary storage(s).") private Long zoneId; @ACL(accessType = AccessType.OperateEntry) @@ -55,7 +55,7 @@ public class PrepareTemplateCmd extends BaseCmd { type = CommandType.UUID, entityType = TemplateResponse.class, required = true, - description = "template ID of the template to be prepared in primary storage(s).") + description = "Template ID of the Template to be prepared in primary storage(s).") private Long templateId; @ACL(accessType = AccessType.OperateEntry) @@ -63,7 +63,7 @@ public class PrepareTemplateCmd extends BaseCmd { type = CommandType.UUID, entityType = StoragePoolResponse.class, required = false, - description = "storage pool ID of the primary storage pool to which the template should be prepared. If it is not provided the template" + + description = "Storage pool ID of the primary storage pool to which the Template should be prepared. If it is not provided the Template" + " is prepared on all the available primary storage pools.") private Long storageId; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/template/RegisterTemplateCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/template/RegisterTemplateCmdByAdmin.java index 91c0dd50e8e3..3eeb9ba7c737 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/template/RegisterTemplateCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/template/RegisterTemplateCmdByAdmin.java @@ -22,7 +22,7 @@ import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd; import org.apache.cloudstack.api.response.TemplateResponse; -@APICommand(name = "registerTemplate", description = "Registers an existing template into the CloudStack cloud.", responseObject = TemplateResponse.class, responseView = ResponseView.Full, +@APICommand(name = "registerTemplate", description = "Registers an existing Template into the CloudStack cloud.", responseObject = TemplateResponse.class, responseView = ResponseView.Full, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class RegisterTemplateCmdByAdmin extends RegisterTemplateCmd implements AdminCmd { } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/template/UpdateTemplateCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/template/UpdateTemplateCmdByAdmin.java index b1dfae3ed83f..b75a2c0755db 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/template/UpdateTemplateCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/template/UpdateTemplateCmdByAdmin.java @@ -22,7 +22,7 @@ import org.apache.cloudstack.api.command.user.template.UpdateTemplateCmd; import org.apache.cloudstack.api.response.TemplateResponse; -@APICommand(name = "updateTemplate", description = "Updates attributes of a template.", responseObject = TemplateResponse.class, responseView = ResponseView.Full, +@APICommand(name = "updateTemplate", description = "Updates attributes of a Template.", responseObject = TemplateResponse.class, responseView = ResponseView.Full, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class UpdateTemplateCmdByAdmin extends UpdateTemplateCmd implements AdminCmd { } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/AddTrafficTypeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/AddTrafficTypeCmd.java index b1810676b744..50abd953e63f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/AddTrafficTypeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/AddTrafficTypeCmd.java @@ -46,10 +46,10 @@ public class AddTrafficTypeCmd extends BaseAsyncCreateCmd { type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, required = true, - description = "the Physical Network ID") + description = "The Physical Network ID") private Long physicalNetworkId; - @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, required = true, description = "the trafficType to be added to the physical network") + @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, required = true, description = "The trafficType to be added to the physical network") private String trafficType; @Parameter(name = ApiConstants.XENSERVER_NETWORK_LABEL, @@ -80,7 +80,7 @@ public class AddTrafficTypeCmd extends BaseAsyncCreateCmd { @Parameter(name = ApiConstants.VLAN, type = CommandType.STRING, description = "The VLAN id to be used for Management traffic by VMware host") private String vlan; - @Parameter(name=ApiConstants.ISOLATION_METHOD, type=CommandType.STRING, description="Used if physical network has multiple isolation types and traffic type is public." + @Parameter(name=ApiConstants.ISOLATION_METHOD, type=CommandType.STRING, description = "Used if physical network has multiple isolation types and traffic type is public." + " Choose which isolation method. Valid options currently 'vlan' or 'vxlan', defaults to 'vlan'.") private String isolationMethod; @@ -148,7 +148,7 @@ public long getEntityOwnerId() { @Override public void execute() { - CallContext.current().setEventDetails("TrafficType Id: " + getEntityId()); + CallContext.current().setEventDetails("Traffic type ID: " + getEntityUuid()); PhysicalNetworkTrafficType result = _networkService.getPhysicalNetworkTrafficType(getEntityId()); if (result != null) { TrafficTypeResponse response = _responseGenerator.createTrafficTypeResponse(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/DeleteTrafficTypeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/DeleteTrafficTypeCmd.java index a1e4ebda09a3..3f07020e5fa2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/DeleteTrafficTypeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/DeleteTrafficTypeCmd.java @@ -38,7 +38,7 @@ public class DeleteTrafficTypeCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TrafficTypeResponse.class, required = true, description = "traffic type id") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TrafficTypeResponse.class, required = true, description = "Traffic type ID") private Long id; ///////////////////////////////////////////////////// @@ -71,7 +71,7 @@ public void execute() { @Override public String getEventDescription() { - return "Deleting Traffic Type: " + getId(); + return "Deleting Traffic Type with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListTrafficMonitorsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListTrafficMonitorsCmd.java index ed42bc42dbd1..03e17631ba8a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListTrafficMonitorsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListTrafficMonitorsCmd.java @@ -38,7 +38,7 @@ public class ListTrafficMonitorsCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "zone Id") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "Zone ID") private long zoneId; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListTrafficTypesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListTrafficTypesCmd.java index d106a736fcab..dc6e0f5dd69d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListTrafficTypesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListTrafficTypesCmd.java @@ -26,14 +26,13 @@ import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.PhysicalNetworkResponse; -import org.apache.cloudstack.api.response.ProviderResponse; import org.apache.cloudstack.api.response.TrafficTypeResponse; import com.cloud.network.PhysicalNetworkTrafficType; import com.cloud.user.Account; import com.cloud.utils.Pair; -@APICommand(name = "listTrafficTypes", description = "Lists traffic types of a given physical network.", responseObject = ProviderResponse.class, since = "3.0.0", +@APICommand(name = "listTrafficTypes", description = "Lists traffic types of a given physical network.", responseObject = TrafficTypeResponse.class, since = "3.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListTrafficTypesCmd extends BaseListCmd { @@ -44,7 +43,7 @@ public class ListTrafficTypesCmd extends BaseListCmd { type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, required = true, - description = "the Physical Network ID") + description = "The Physical Network ID") private Long physicalNetworkId; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/UpdateTrafficTypeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/UpdateTrafficTypeCmd.java index c7b3c2b433b4..29b06a3b5259 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/UpdateTrafficTypeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/UpdateTrafficTypeCmd.java @@ -39,7 +39,7 @@ public class UpdateTrafficTypeCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TrafficTypeResponse.class, required = true, description = "traffic type id") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TrafficTypeResponse.class, required = true, description = "Traffic type ID") private Long id; @Parameter(name = ApiConstants.XENSERVER_NETWORK_LABEL, @@ -118,7 +118,7 @@ public void execute() { @Override public String getEventDescription() { - return "Updating Traffic Type: " + getId(); + return "Updating Traffic Type with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/CreateUserCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/CreateUserCmd.java index e2a2baecc866..684103cf8d39 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/CreateUserCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/CreateUserCmd.java @@ -26,6 +26,7 @@ import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.UserResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang3.StringUtils; import com.cloud.user.Account; @@ -52,13 +53,13 @@ public class CreateUserCmd extends BaseCmd { description = "Creates the user under the specified domain. Has to be accompanied with the account parameter") private Long domainId; - @Parameter(name = ApiConstants.EMAIL, type = CommandType.STRING, required = true, description = "email") + @Parameter(name = ApiConstants.EMAIL, type = CommandType.STRING, required = true, description = "Email") private String email; - @Parameter(name = ApiConstants.FIRSTNAME, type = CommandType.STRING, required = true, description = "firstname") + @Parameter(name = ApiConstants.FIRSTNAME, type = CommandType.STRING, required = true, description = "Firstname") private String firstname; - @Parameter(name = ApiConstants.LASTNAME, type = CommandType.STRING, required = true, description = "lastname") + @Parameter(name = ApiConstants.LASTNAME, type = CommandType.STRING, required = true, description = "Lastname") private String lastname; @Parameter(name = ApiConstants.PASSWORD, @@ -78,6 +79,12 @@ public class CreateUserCmd extends BaseCmd { @Parameter(name = ApiConstants.USER_ID, type = CommandType.STRING, description = "User UUID, required for adding account from external provisioning system") private String userUUID; + @Parameter(name = ApiConstants.PASSWORD_CHANGE_REQUIRED, + type = CommandType.BOOLEAN, + description = "Provide true to mandate the User to reset password on next login.", + since = "4.23.0") + private Boolean passwordChangeRequired; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -118,6 +125,10 @@ public String getUserUUID() { return userUUID; } + public Boolean isPasswordChangeRequired() { + return BooleanUtils.isTrue(passwordChangeRequired); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -147,7 +158,7 @@ public void execute() { CallContext.current().setEventDetails("UserName: " + getUserName() + ", FirstName :" + getFirstName() + ", LastName: " + getLastName()); User user = _accountService.createUser(getUserName(), getPassword(), getFirstName(), getLastName(), getEmail(), getTimezone(), getAccountName(), getDomainId(), - getUserUUID()); + getUserUUID(), isPasswordChangeRequired()); if (user != null) { UserResponse response = _responseGenerator.createUserResponse(user); response.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/DeleteUserCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/DeleteUserCmd.java index ddf21affb535..01886187f9b9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/DeleteUserCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/DeleteUserCmd.java @@ -42,7 +42,7 @@ public class DeleteUserCmd extends BaseCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserResponse.class, required = true, description = "id of the user to be deleted") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserResponse.class, required = true, description = "ID of the user to be deleted") private Long id; @Inject @@ -82,7 +82,7 @@ public ApiCommandResourceType getApiResourceType() { @Override public void execute() { - CallContext.current().setEventDetails("UserId: " + getId()); + CallContext.current().setEventDetails("User ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _regionService.deleteUser(this); if (!result) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete user"); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/DeleteUserKeysCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/DeleteUserKeysCmd.java new file mode 100644 index 000000000000..6cf55514ba36 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/DeleteUserKeysCmd.java @@ -0,0 +1,81 @@ +// 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.user; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; +import org.apache.cloudstack.acl.apikeypair.ApiKeyPair; +import org.apache.cloudstack.api.ACL; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.ApiKeyPairResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + +@APICommand(name = "deleteUserKeys", description = "Deletes a keypair from a user", responseObject = SuccessResponse.class, + since = "4.23.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class DeleteUserKeysCmd extends BaseAsyncCmd { + @ACL + @Parameter(name = ApiConstants.KEYPAIR_ID, type = CommandType.UUID, entityType = ApiKeyPairResponse.class, required = true, description = "ID of the keypair to be deleted.") + private Long id; + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.User; + } + + @Override + public long getEntityOwnerId() { + ApiKeyPair keyPair = apiKeyPairService.findById(id); + if (keyPair != null) { + return keyPair.getAccountId(); + } + return Account.ACCOUNT_ID_SYSTEM; + } + + public Long getId() { + return id; + } + + @Override + public void execute() { + _accountService.deleteApiKey(this); + + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_DELETE_SECRET_API_KEY; + } + + @Override + public String getEventDescription() { + ApiKeyPair keyPair = apiKeyPairService.findById(id); + return String.format("Deleting API key pair with ID [%s]%s", + keyPair == null ? id : keyPair.getUuid(), + keyPair == null ? "." : String.format(" and name [%s].", keyPair.getName())); + } + + @Override + public Long getSyncObjId() { + return getId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/DisableUserCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/DisableUserCmd.java index 974c1c7bebed..cc2bc0906a24 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/DisableUserCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/DisableUserCmd.java @@ -78,12 +78,12 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - return "disabling user: " + getId(); + return "Disabling User with ID: " + getResourceUuid(ApiConstants.ID); } @Override public void execute() { - CallContext.current().setEventDetails("UserId: " + getId()); + CallContext.current().setEventDetails("User ID: " + getResourceUuid(ApiConstants.ID)); UserAccount user = _regionService.disableUser(this); if (user != null) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/EnableUserCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/EnableUserCmd.java index 77d8d530daf2..9141a9bccf8a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/EnableUserCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/EnableUserCmd.java @@ -71,7 +71,7 @@ public long getEntityOwnerId() { @Override public void execute() { - CallContext.current().setEventDetails("UserId: " + getId()); + CallContext.current().setEventDetails("User ID: " + getResourceUuid(ApiConstants.ID)); UserAccount user = _regionService.enableUser(this); if (user != null) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/GetUserCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/GetUserCmd.java index 3427cef33661..a3dcf08c3a31 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/GetUserCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/GetUserCmd.java @@ -22,7 +22,7 @@ import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.response.UserResponse; - +import org.apache.cloudstack.api.ApiArgValidator; import com.cloud.exception.InvalidParameterValueException; import com.cloud.user.UserAccount; @@ -35,7 +35,7 @@ public class GetUserCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.USER_API_KEY, type = CommandType.STRING, required = true, description = "API key of the user") + @Parameter(name = ApiConstants.USER_API_KEY, type = CommandType.STRING, required = true, description = "API key of the user", validations = {ApiArgValidator.NotNullOrEmpty}) private String apiKey; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/GetUserKeysCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/GetUserKeysCmd.java index cdd239f72b57..a651befcff7e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/GetUserKeysCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/GetUserKeysCmd.java @@ -26,39 +26,39 @@ import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; -import org.apache.cloudstack.api.response.RegisterResponse; +import org.apache.cloudstack.api.response.RegisterUserKeyResponse; import org.apache.cloudstack.api.response.UserResponse; import java.util.Map; @APICommand(name = "getUserKeys", - description = "This command allows the user to query the seceret and API keys for the account", - responseObject = RegisterResponse.class, - requestHasSensitiveInfo = false, - responseHasSensitiveInfo = true, - authorized = {RoleType.User, RoleType.Admin, RoleType.DomainAdmin, RoleType.ResourceAdmin}, - since = "4.10.0") - -public class GetUserKeysCmd extends BaseCmd{ - - @Parameter(name= ApiConstants.ID, type = CommandType.UUID, entityType = UserResponse.class, required = true, description = "ID of the user whose keys are required") + description = "Queries the last registered secret and API keys of a user.", + responseObject = RegisterUserKeyResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = true, + authorized = {RoleType.User, RoleType.Admin, RoleType.DomainAdmin, RoleType.ResourceAdmin}, + since = "4.10.0") +public class GetUserKeysCmd extends BaseCmd { + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserResponse.class, required = true, description = "ID of the user whose keys are required") private Long id; - - public Long getID(){ + public Long getId() { return id; - }public long getEntityOwnerId(){ - User user = _entityMgr.findById(User.class, getID()); - if(user != null){ + } + + public long getEntityOwnerId() { + User user = _entityMgr.findById(User.class, getId()); + if (user != null) { return user.getAccountId(); } - else return Account.ACCOUNT_ID_SYSTEM; + return Account.ACCOUNT_ID_SYSTEM; } - public void execute(){ + + public void execute() { Pair> keys = _accountService.getKeys(this); - RegisterResponse response = new RegisterResponse(); - if(keys != null){ + RegisterUserKeyResponse response = new RegisterUserKeyResponse(); + if (keys != null){ response.setApiKeyAccess(keys.first()); response.setApiKey(keys.second().get("apikey")); response.setSecretKey(keys.second().get("secretkey")); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/ListUserKeyRulesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/ListUserKeyRulesCmd.java new file mode 100644 index 000000000000..4345cae92911 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/ListUserKeyRulesCmd.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.api.command.admin.user; + + +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import org.apache.cloudstack.acl.apikeypair.ApiKeyPair; +import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission; +import org.apache.cloudstack.api.ACL; +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.ApiKeyPairResponse; +import org.apache.cloudstack.api.response.BaseRolePermissionResponse; +import org.apache.cloudstack.api.response.ListResponse; + +import java.util.List; + +@APICommand(name = "listUserKeyRules", + description = "Lists the rules defined for a API key pair.", + responseObject = BaseRolePermissionResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + since = "4.23.0") +public class ListUserKeyRulesCmd extends BaseCmd { + @ACL + @Parameter(name = ApiConstants.KEYPAIR_ID, type = CommandType.UUID, entityType = ApiKeyPairResponse.class, description = "ID of the key pair.", required = true) + private Long id; + + public Long getId() { + return id; + } + + public long getEntityOwnerId() { + ApiKeyPair keyPair = apiKeyPairService.findById(getId()); + if (keyPair != null) { + return keyPair.getAccountId(); + } + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException { + List permissions = _accountService.listKeyRules(this); + ListResponse response = _responseGenerator.createKeypairPermissionsResponse(permissions); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/ListUserKeysCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/ListUserKeysCmd.java new file mode 100644 index 000000000000..ded05d6381a0 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/ListUserKeysCmd.java @@ -0,0 +1,101 @@ +// 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.user; + + +import com.cloud.user.Account; +import com.cloud.user.UserAccount; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.acl.apikeypair.ApiKeyPair; +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.ListResponse; +import org.apache.cloudstack.api.response.ApiKeyPairResponse; +import org.apache.cloudstack.api.response.UserResponse; + +@APICommand(name = "listUserKeys", + description = "Lists the API key pairs (API and secret keys) of a user.", + responseObject = ApiKeyPairResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = true, + authorized = {RoleType.User, RoleType.Admin, RoleType.DomainAdmin, RoleType.ResourceAdmin}, + since = "4.23.0") +public class ListUserKeysCmd extends BaseListCmd { + @ACL + @Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class, description = "ID of the user that owns the keys.") + private Long userId; + + @ACL + @Parameter(name = ApiConstants.KEYPAIR_ID, type = CommandType.UUID, entityType = ApiKeyPairResponse.class, description = "ID of the key pair.") + private Long keyPairId; + + @Parameter(name = ApiConstants.API_KEY_FILTER, type = CommandType.STRING, description = "API key of the key pair.") + private String apiKeyFilter; + + @Parameter(name = ApiConstants.SHOW_PERMISSIONS, type = CommandType.BOOLEAN, description = "Whether API Key rules should be returned. Defaults to false.") + private boolean showPermissions = false; + + @Parameter(name = ApiConstants.LIST_ALL, type = CommandType.BOOLEAN, authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin}, + description = "Lists all API key pairs of users that are accessible by the calling account. Only available for administrators. Defaults to false.") + private boolean listAll = false; + + public Long getUserId() { + return userId; + } + + public Long getKeyId() { + return keyPairId; + } + + public String getApiKeyFilter() { + return apiKeyFilter; + } + + public Boolean getShowPermissions() { + return showPermissions; + } + + public boolean getListAll() { + return listAll; + } + + public long getEntityOwnerId() { + if (getKeyId() != null) { + ApiKeyPair keypair = apiKeyPairService.findById(getKeyId()); + if (keypair != null) { + return keypair.getAccountId(); + } + } else if (getUserId() != null) { + UserAccount userAccount = _accountService.getUserAccountById(getUserId()); + if (userAccount != null) { + return userAccount.getAccountId(); + } + } + return Account.ACCOUNT_ID_SYSTEM; + } + + public void execute() { + ListResponse finalResponse = _accountService.listKeys(this); + finalResponse.setObjectName("userkeys"); + finalResponse.setResponseName(getCommandName()); + setResponseObject(finalResponse); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/ListUsersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/ListUsersCmd.java index 2f29b1ec1e4d..0fe83fd0796c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/ListUsersCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/ListUsersCmd.java @@ -63,7 +63,7 @@ public class ListUsersCmd extends BaseListAccountResourcesCmd implements UserCmd private String apiKeyAccess; @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, - description = "flag to display the resource icon for users") + description = "Flag to display the resource icon for users") private Boolean showIcon; @Parameter(name = ApiConstants.USER_SOURCE, type = CommandType.STRING, since = "4.21.0.0", diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/MoveUserCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/MoveUserCmd.java index e57258a45711..aab20f108f9e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/MoveUserCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/MoveUserCmd.java @@ -54,7 +54,7 @@ public class MoveUserCmd extends BaseCmd { type = CommandType.UUID, entityType = UserResponse.class, required = true, - description = "id of the user to be moved.") + description = "ID of the user to be moved.") private Long id; @Parameter(name = ApiConstants.ACCOUNT, @@ -116,7 +116,7 @@ public void execute() { Preconditions.checkNotNull(getId(),"I have to have an user to move!"); Preconditions.checkState(ObjectUtils.anyNotNull(getAccountId(),getAccountName()),"provide either an account name or an account id!"); - CallContext.current().setEventDetails("UserId: " + getId()); + CallContext.current().setEventDetails("User ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _regionService.moveUser(this); if (result) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/RegisterCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/RegisterCmd.java deleted file mode 100644 index b3e7d2bec821..000000000000 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/RegisterCmd.java +++ /dev/null @@ -1,93 +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.api.command.admin.user; - -import org.apache.cloudstack.api.ApiCommandResourceType; - -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.RegisterResponse; -import org.apache.cloudstack.api.response.UserResponse; - -import com.cloud.user.Account; -import com.cloud.user.User; - -@APICommand(name = "registerUserKeys", - responseObject = RegisterResponse.class, - description = "This command allows a user to register for the developer API, returning a secret key and an API key. This request is made through the integration API port, so it is a privileged command and must be made on behalf of a user. It is up to the implementer just how the username and password are entered, and then how that translates to an integration API request. Both secret key and API key should be returned to the user", - requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) -public class RegisterCmd extends BaseCmd { - - - ///////////////////////////////////////////////////// - //////////////// API parameters ///////////////////// - ///////////////////////////////////////////////////// - - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserResponse.class, required = true, description = "User id") - private Long id; - - ///////////////////////////////////////////////////// - /////////////////// Accessors /////////////////////// - ///////////////////////////////////////////////////// - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - ///////////////////////////////////////////////////// - /////////////// API Implementation/////////////////// - ///////////////////////////////////////////////////// - - @Override - public long getEntityOwnerId() { - User user = _entityMgr.findById(User.class, getId()); - if (user != null) { - return user.getAccountId(); - } - - return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked - } - - @Override - public Long getApiResourceId() { - return id; - } - - @Override - public ApiCommandResourceType getApiResourceType() { - return ApiCommandResourceType.User; - } - - @Override - public void execute() { - String[] keys = _accountService.createApiKeyAndSecretKey(this); - RegisterResponse response = new RegisterResponse(); - if (keys != null) { - response.setApiKey(keys[0]); - response.setSecretKey(keys[1]); - } - response.setObjectName("userkeys"); - response.setResponseName(getCommandName()); - this.setResponseObject(response); - } -} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/RegisterUserKeysCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/RegisterUserKeysCmd.java new file mode 100644 index 000000000000..28c79517f4b9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/RegisterUserKeysCmd.java @@ -0,0 +1,212 @@ +// 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.user; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; +import com.cloud.user.User; +import org.apache.cloudstack.acl.Rule; +import org.apache.cloudstack.acl.apikeypair.ApiKeyPair; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.commons.lang3.StringUtils; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.ApiKeyPairResponse; +import org.apache.cloudstack.api.response.UserResponse; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@APICommand(name = "registerUserKeys", + responseObject = ApiKeyPairResponse.class, + description = "Registers an API key pair (API and secret keys) for a user.", + requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) +public class RegisterUserKeysCmd extends BaseAsyncCmd { + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserResponse.class, required = true, description = "ID of the user.") + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "API key pair name.") + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "API key pair description.", length = 1024) + private String description; + + @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, description = "Start date of the API key pair. " + + ApiConstants.PARAMETER_DESCRIPTION_START_DATE_POSSIBLE_FORMATS) + private Date startDate; + + @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, description = "Expiration date of the API key pair. " + + ApiConstants.PARAMETER_DESCRIPTION_END_DATE_POSSIBLE_FORMATS) + private Date endDate; + + @Parameter(name = ApiConstants.RULES, type = CommandType.MAP, description = "The rules of the API key pair. If no rules are informed, " + + "defaults to allowing all account permissions. Otherwise, only the explicitly informed permissions for the key pair will be " + + "considered. Lower indexed rules take precedence over higher. Thus, in the following example: " + + "\"rules[0].rule=deleteUserKeys rules[0].permission=deny rules[1].rule=*UserKey* rules[1].permission=allow\", all rules matching " + + "the expression \"*UserKeys*\" will be allowed, except for \"deleteUserKeys\".") + private Map rules; + + public void setUserId(Long userId) { + this.id = userId; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Date getStartDate() { + return startDate; + } + + public void setStartDate(Date startDate) { + this.startDate = startDate; + } + + public Date getEndDate() { + return endDate; + } + + public void setEndDate(Date endDate) { + this.endDate = endDate; + } + + public void setRules(Map rules) { + this.rules = rules; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + public List> getRules() { + List> rulesDetails = new ArrayList<>(); + + if (rules == null) { + return rulesDetails; + } + + for (Object ruleObject : rules.values()) { + HashMap detail = (HashMap) ruleObject; + Map ruleDetails = new HashMap<>(); + + String rule = detail.get(ApiConstants.RULE); + if (StringUtils.isEmpty(rule)) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Empty rule has been provided in the [rules] parameter."); + } + + String permission = detail.get(ApiConstants.PERMISSION); + if (StringUtils.isEmpty(permission)) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, String.format("Rule [%s] has no permission associated with it," + + " please specify if it is either [allow] or [deny].", rule)); + } + ruleDetails.put(ApiConstants.RULE, new Rule(rule)); + ruleDetails.put(ApiConstants.PERMISSION, roleService.getRolePermission(permission)); + + String description = detail.get(ApiConstants.DESCRIPTION); + if (StringUtils.isNotEmpty(description)) { + if (description.length() > 255) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Rule description cannot be longer than 255 characters."); + } + ruleDetails.put(ApiConstants.DESCRIPTION, description); + } + + rulesDetails.add(ruleDetails); + } + return rulesDetails; + } + + @Override + public long getEntityOwnerId() { + User user = _entityMgr.findById(User.class, getUserId()); + List accessibleUsers = _queryService.searchForAccessibleUsers(); + if (user != null && accessibleUsers.stream().anyMatch(u -> u == user.getId())) { + return user.getAccountId(); + } + return Account.ACCOUNT_ID_SYSTEM; + } + + public Long getUserId() { + return id; + } + + @Override + public Long getApiResourceId() { + User user = _entityMgr.findById(User.class, getUserId()); + if (user != null) { + return user.getId(); + } + return null; + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.User; + } + + @Override + public void execute() { + ApiKeyPair apiKeyPair = _accountService.createApiKeyAndSecretKey(this); + ApiKeyPairResponse response = new ApiKeyPairResponse(); + if (apiKeyPair != null) { + response = _responseGenerator.createKeyPairResponse(apiKeyPair); + } + response.setObjectName("userkeys"); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_REGISTER_FOR_SECRET_API_KEY; + } + + @Override + public String getEventDescription() { + String userUuid = getResourceUuid(ApiConstants.ID); + return String.format("Registering API keypair for user [%s].", userUuid == null ? id : userUuid); + } + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.user; + } + + @Override + public Long getSyncObjId() { + return getUserId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/UpdateUserCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/UpdateUserCmd.java index 3d7f51ae2204..3f5ce2415022 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/UpdateUserCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/UpdateUserCmd.java @@ -29,6 +29,7 @@ import org.apache.cloudstack.api.response.UserResponse; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.region.RegionService; +import org.apache.commons.lang.BooleanUtils; import com.cloud.user.Account; import com.cloud.user.User; @@ -38,24 +39,26 @@ requestHasSensitiveInfo = true, responseHasSensitiveInfo = true) public class UpdateUserCmd extends BaseCmd { + @Inject + private RegionService _regionService; ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.USER_API_KEY, type = CommandType.STRING, description = "The API key for the user. Must be specified with userSecretKey") - private String apiKey; + @Parameter(name = ApiConstants.USER_API_KEY, type = CommandType.STRING, description = "Updates the latest API key of the user. Must be specified with usersecretkey") + private String userApiKey; - @Parameter(name = ApiConstants.EMAIL, type = CommandType.STRING, description = "email") + @Parameter(name = ApiConstants.EMAIL, type = CommandType.STRING, description = "Email") private String email; - @Parameter(name = ApiConstants.FIRSTNAME, type = CommandType.STRING, description = "first name") + @Parameter(name = ApiConstants.FIRSTNAME, type = CommandType.STRING, description = "First name") private String firstname; @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserResponse.class, required = true, description = "User uuid") private Long id; - @Parameter(name = ApiConstants.LASTNAME, type = CommandType.STRING, description = "last name") + @Parameter(name = ApiConstants.LASTNAME, type = CommandType.STRING, description = "Last name") private String lastname; @Parameter(name = ApiConstants.PASSWORD, @@ -67,8 +70,8 @@ public class UpdateUserCmd extends BaseCmd { @Parameter(name = ApiConstants.CURRENT_PASSWORD, type = CommandType.STRING, description = "Current password that was being used by the user. You must inform the current password when updating the password.", acceptedOnAdminPort = false) private String currentPassword; - @Parameter(name = ApiConstants.USER_SECRET_KEY, type = CommandType.STRING, description = "The secret key for the user. Must be specified with userApiKey") - private String secretKey; + @Parameter(name = ApiConstants.USER_SECRET_KEY, type = CommandType.STRING, description = "Updates the latest secret key of the user. Must be specified with userapikey.") + private String userSecretKey; @Parameter(name = ApiConstants.API_KEY_ACCESS, type = CommandType.STRING, description = "Determines if Api key access for this user is enabled, disabled or inherits the value from its parent, the owning account", since = "4.20.1.0", authorized = {RoleType.Admin}) private String apiKeyAccess; @@ -85,15 +88,18 @@ public class UpdateUserCmd extends BaseCmd { "This parameter is only used to mandate 2FA, not to disable 2FA", since = "4.18.0.0") private Boolean mandate2FA; - @Inject - private RegionService _regionService; + @Parameter(name = ApiConstants.PASSWORD_CHANGE_REQUIRED, + type = CommandType.BOOLEAN, + description = "Provide true to mandate the User to reset password on next login.", + since = "4.23.0") + private Boolean passwordChangeRequired; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// public String getApiKey() { - return apiKey; + return userApiKey; } public String getEmail() { @@ -121,7 +127,7 @@ public String getCurrentPassword() { } public String getSecretKey() { - return secretKey; + return userSecretKey; } public String getApiKeyAccess() { @@ -156,7 +162,7 @@ public long getEntityOwnerId() { @Override public void execute() { - CallContext.current().setEventDetails("UserId: " + getId()); + CallContext.current().setEventDetails("User ID: " + getResourceUuid(ApiConstants.ID)); UserAccount user = _regionService.updateUser(this); if (user != null) { @@ -193,4 +199,8 @@ public Long getApiResourceId() { public ApiCommandResourceType getApiResourceType() { return ApiCommandResourceType.User; } + + public Boolean isPasswordChangeRequired() { + return BooleanUtils.isTrue(passwordChangeRequired); + } } 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 5cb384925e83..67278991383c 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 @@ -19,6 +19,7 @@ import com.cloud.configuration.ConfigurationService; import com.cloud.network.Network; import com.cloud.utils.net.NetUtils; +import com.cloud.utils.StringUtils; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -53,66 +54,66 @@ public class CreateVlanIpRangeCmd extends BaseCmd { @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, - description = "account who will own the VLAN. If VLAN is Zone wide, this parameter should be omitted") + description = "Account who will own the VLAN. If VLAN is Zone wide, this parameter should be omitted") private String accountName; @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, - description = "project who will own the VLAN. If VLAN is Zone wide, this parameter should be omitted") + description = "Project who will own the VLAN. If VLAN is Zone wide, this parameter should be omitted") private Long projectId; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "domain ID of the account owning a VLAN") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "Domain ID of the Account owning a VLAN") private Long domainId; - @Parameter(name = ApiConstants.END_IP, type = CommandType.STRING, description = "the ending IP address in the VLAN IP range") + @Parameter(name = ApiConstants.END_IP, type = CommandType.STRING, description = "The ending IP address in the VLAN IP range") private String endIp; - @Parameter(name = ApiConstants.FOR_VIRTUAL_NETWORK, type = CommandType.BOOLEAN, description = "true if VLAN is of Virtual type, false if Direct") + @Parameter(name = ApiConstants.FOR_VIRTUAL_NETWORK, type = CommandType.BOOLEAN, description = "True if VLAN is of Virtual type, false if Direct") private Boolean forVirtualNetwork; - @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, description = "the gateway of the VLAN IP range") + @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, description = "The gateway of the VLAN IP range") private String gateway; - @Parameter(name = ApiConstants.NETMASK, type = CommandType.STRING, description = "the netmask of the VLAN IP range") + @Parameter(name = ApiConstants.NETMASK, type = CommandType.STRING, description = "The netmask of the VLAN IP range") private String netmask; @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, - description = "optional parameter. Have to be specified for Direct Untagged vlan only.") + description = "Optional parameter. Have to be specified for Direct Untagged vlan only.") private Long podId; - @Parameter(name = ApiConstants.START_IP, type = CommandType.STRING, description = "the beginning IP address in the VLAN IP range") + @Parameter(name = ApiConstants.START_IP, type = CommandType.STRING, description = "The beginning IP address in the VLAN IP range") private String startIp; - @Parameter(name = ApiConstants.VLAN, type = CommandType.STRING, description = "the ID or VID of the VLAN. If not specified," + @Parameter(name = ApiConstants.VLAN, type = CommandType.STRING, description = "The ID or VID of the VLAN. If not specified," + " will be defaulted to the vlan of the network or if vlan of the network is null - to Untagged") private String vlan; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the Zone ID of the VLAN IP range") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The Zone ID of the VLAN IP range") private Long zoneId; - @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "the network id") + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "The Network ID") private Long networkID; - @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, description = "the physical network id") + @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, description = "The physical network ID") private Long physicalNetworkId; - @Parameter(name = ApiConstants.START_IPV6, type = CommandType.STRING, description = "the beginning IPv6 address in the IPv6 network range") + @Parameter(name = ApiConstants.START_IPV6, type = CommandType.STRING, description = "The beginning IPv6 address in the IPv6 network range") private String startIpv6; - @Parameter(name = ApiConstants.END_IPV6, type = CommandType.STRING, description = "the ending IPv6 address in the IPv6 network range") + @Parameter(name = ApiConstants.END_IPV6, type = CommandType.STRING, description = "The ending IPv6 address in the IPv6 network range") private String endIpv6; - @Parameter(name = ApiConstants.IP6_GATEWAY, type = CommandType.STRING, description = "the gateway of the IPv6 network. Required " + @Parameter(name = ApiConstants.IP6_GATEWAY, type = CommandType.STRING, description = "The gateway of the IPv6 network. Required " + "for Shared networks and Isolated networks when it belongs to VPC") private String ip6Gateway; - @Parameter(name = ApiConstants.IP6_CIDR, type = CommandType.STRING, description = "the CIDR of IPv6 network, must be at least /64") + @Parameter(name = ApiConstants.IP6_CIDR, type = CommandType.STRING, description = "The CIDR of IPv6 network, must be at least /64") private String ip6Cidr; - @Parameter(name = ApiConstants.FOR_SYSTEM_VMS, type = CommandType.BOOLEAN, description = "true if IP range is set to system vms, false if not") + @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.PROVIDER, type = CommandType.STRING, description = "Provider name for which the IP range is reserved for", since = "4.21.0") @@ -163,7 +164,7 @@ public Network.Provider getProvider() { } public String getVlan() { - if ((vlan == null || vlan.isEmpty()) && !ConfigurationService.IsIpRangeForProvider(getProvider())) { + if (StringUtils.isBlank(vlan) && !ConfigurationService.IsIpRangeForProvider(getProvider())) { vlan = "untagged"; } return vlan; @@ -231,13 +232,13 @@ public void execute() throws ResourceUnavailableException, ResourceAllocationExc response.setResponseName(getCommandName()); this.setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create vlan ip range"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create VLAN IP range"); } } catch (ConcurrentOperationException ex) { logger.warn("Exception: ", ex); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); } catch (InsufficientCapacityException ex) { - logger.info(ex); + logger.error(ex); throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ex.getMessage()); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/DedicatePublicIpRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/DedicatePublicIpRangeCmd.java index cac029f3aa12..d4872fa369a6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/DedicatePublicIpRangeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/DedicatePublicIpRangeCmd.java @@ -41,20 +41,20 @@ public class DedicatePublicIpRangeCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VlanIpRangeResponse.class, required = true, description = "the id of the VLAN IP range") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VlanIpRangeResponse.class, required = true, description = "The ID of the VLAN IP range") private Long id; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "account who will own the VLAN") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "Account who will own the VLAN") private String accountName; - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "project who will own the VLAN") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Project who will own the VLAN") private Long projectId; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, required = true, - description = "domain ID of the account owning a VLAN") + description = "Domain ID of the account owning a VLAN") private Long domainId; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/DeleteVlanIpRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/DeleteVlanIpRangeCmd.java index 7ab0b053004c..768b6ecf4192 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/DeleteVlanIpRangeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/DeleteVlanIpRangeCmd.java @@ -37,7 +37,7 @@ public class DeleteVlanIpRangeCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VlanIpRangeResponse.class, required = true, description = "the id of the VLAN IP range") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VlanIpRangeResponse.class, required = true, description = "The ID of the VLAN IP range") private Long id; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/ListVlanIpRangesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/ListVlanIpRangesCmd.java index c11b505c684d..875b36f5fe9b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/ListVlanIpRangesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/ListVlanIpRangesCmd.java @@ -47,41 +47,41 @@ public class ListVlanIpRangesCmd extends BaseListCmd { @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, - description = "the account with which the VLAN IP range is associated. Must be used with the domainId parameter.") + description = "The account with which the VLAN IP range is associated. Must be used with the domainId parameter.") private String accountName; - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "project who will own the VLAN") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Project who will own the VLAN") private Long projectId; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "the domain ID with which the VLAN IP range is associated. If used with the account parameter, " + + description = "The domain ID with which the VLAN IP range is associated. If used with the account parameter, " + "returns all VLAN IP ranges for that account in the specified domain.") private Long domainId; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VlanIpRangeResponse.class, required = false, description = "the ID of the VLAN IP range") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VlanIpRangeResponse.class, required = false, description = "The ID of the VLAN IP range") private Long id; - @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "the Pod ID of the VLAN IP range") + @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "The Pod ID of the VLAN IP range") private Long podId; - @Parameter(name = ApiConstants.VLAN, type = CommandType.STRING, description = "the ID or VID of the VLAN. Default is an \"untagged\" VLAN.") + @Parameter(name = ApiConstants.VLAN, type = CommandType.STRING, description = "The ID or VID of the VLAN. Default is an \"untagged\" VLAN.") private String vlan; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the Zone ID of the VLAN IP range") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The Zone ID of the VLAN IP range") private Long zoneId; - @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "network id of the VLAN IP range") + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "Network ID of the VLAN IP range") private Long networkId; - @Parameter(name = ApiConstants.FOR_VIRTUAL_NETWORK, type = CommandType.BOOLEAN, description = "true if VLAN is of Virtual type, false if Direct") + @Parameter(name = ApiConstants.FOR_VIRTUAL_NETWORK, type = CommandType.BOOLEAN, description = "True if VLAN is of Virtual type, false if Direct") private Boolean forVirtualNetwork; @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, - description = "physical network id of the VLAN IP range") + description = "Physical Network ID of the VLAN IP range") private Long physicalNetworkId; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/ReleasePublicIpRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/ReleasePublicIpRangeCmd.java index be4cea41cd89..e369516f5eec 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/ReleasePublicIpRangeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/ReleasePublicIpRangeCmd.java @@ -37,7 +37,7 @@ public class ReleasePublicIpRangeCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VlanIpRangeResponse.class, required = true, description = "the id of the Public IP range") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VlanIpRangeResponse.class, required = true, description = "The ID of the Public IP range") private Long id; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/UpdateVlanIpRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/UpdateVlanIpRangeCmd.java index df6d99f8e2af..be8e8b39d4ed 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/UpdateVlanIpRangeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/UpdateVlanIpRangeCmd.java @@ -45,35 +45,35 @@ public class UpdateVlanIpRangeCmd extends BaseCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VlanIpRangeResponse.class, required = true, - description = "the UUID of the VLAN IP range") + description = "The UUID of the VLAN IP range") private Long id; - @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, description = "the gateway of the VLAN IP range") + @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, description = "The gateway of the VLAN IP range") private String gateway; - @Parameter(name = ApiConstants.NETMASK, type = CommandType.STRING, description = "the netmask of the VLAN IP range") + @Parameter(name = ApiConstants.NETMASK, type = CommandType.STRING, description = "The netmask of the VLAN IP range") private String netmask; - @Parameter(name = ApiConstants.START_IP, type = CommandType.STRING, description = "the beginning IP address in the VLAN IP range") + @Parameter(name = ApiConstants.START_IP, type = CommandType.STRING, description = "The beginning IP address in the VLAN IP range") private String startIp; @Parameter(name = ApiConstants.END_IP, type = CommandType.STRING, - description = "the ending IP address in the VLAN IP range") + description = "The ending IP address in the VLAN IP range") private String endIp; - @Parameter(name = ApiConstants.START_IPV6, type = CommandType.STRING, description = "the beginning IPv6 address in the IPv6 network range") + @Parameter(name = ApiConstants.START_IPV6, type = CommandType.STRING, description = "The beginning IPv6 address in the IPv6 Network range") private String startIpv6; - @Parameter(name = ApiConstants.END_IPV6, type = CommandType.STRING, description = "the ending IPv6 address in the IPv6 network range") + @Parameter(name = ApiConstants.END_IPV6, type = CommandType.STRING, description = "The ending IPv6 address in the IPv6 Network range") private String endIpv6; - @Parameter(name = ApiConstants.IP6_GATEWAY, type = CommandType.STRING, description = "the gateway of the IPv6 network") + @Parameter(name = ApiConstants.IP6_GATEWAY, type = CommandType.STRING, description = "The gateway of the IPv6 Network") private String ip6Gateway; - @Parameter(name = ApiConstants.IP6_CIDR, type = CommandType.STRING, description = "the CIDR of IPv6 network, must be at least /64") + @Parameter(name = ApiConstants.IP6_CIDR, type = CommandType.STRING, description = "The CIDR of IPv6 Network, must be at least /64") private String ip6Cidr; - @Parameter(name = ApiConstants.FOR_SYSTEM_VMS, type = CommandType.BOOLEAN, description = "true if IP range is set to system vms, false if not") + @Parameter(name = ApiConstants.FOR_SYSTEM_VMS, type = CommandType.BOOLEAN, description = "True if IP range is set to System VM, false if not") private Boolean forSystemVms; ///////////////////////////////////////////////////// @@ -144,7 +144,7 @@ public void execute() throws ResourceUnavailableException, ResourceAllocationExc response.setResponseName(getCommandName()); this.setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to Update vlan ip range"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to Update VLAN IP range"); } } catch (ConcurrentOperationException ex) { logger.warn("Exception: ", ex); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/AddNicToVMCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/AddNicToVMCmdByAdmin.java index 7a8c409c8a8f..ce421433d50b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/AddNicToVMCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/AddNicToVMCmdByAdmin.java @@ -25,6 +25,6 @@ import com.cloud.vm.VirtualMachine; -@APICommand(name = "addNicToVirtualMachine", description = "Adds VM to specified network by creating a NIC", responseObject = UserVmResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, +@APICommand(name = "addNicToVirtualMachine", description = "Adds Instance to specified network by creating a NIC", responseObject = UserVmResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class AddNicToVMCmdByAdmin extends AddNicToVMCmd implements AdminCmd {} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/AssignVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/AssignVMCmd.java index 6f6a5237b3d1..50ff97ac676f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/AssignVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/AssignVMCmd.java @@ -39,7 +39,7 @@ import com.cloud.vm.VirtualMachine; @APICommand(name = "assignVirtualMachine", - description = "Change ownership of a VM from one account to another. This API is available for Basic zones with security groups and Advanced zones with guest networks. A root administrator can reassign a VM from any account to any other account in any domain. A domain administrator can reassign a VM to any account in the same domain.", + description = "Change ownership of an Instance from one account to another. This API is available for Basic zones with security groups and Advanced zones with guest networks. A root administrator can reassign an Instance from any account to any other account in any domain. A domain administrator can reassign an Instance to any account in the same domain.", responseObject = UserVmResponse.class, since = "3.0.0", entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, @@ -55,16 +55,16 @@ public class AssignVMCmd extends BaseCmd { type = CommandType.UUID, entityType = UserVmResponse.class, required = true, - description = "id of the VM to be moved") + description = "ID of the Instance to be moved") private Long virtualMachineId; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "account name of the new VM owner.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "Account name of the new Instance owner.") private String accountName; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "domain id of the new VM owner.") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "Domain id of the new Instance owner.") private Long domainId; - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "an optional project for the new VM owner.") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "An optional project for the new Instance owner.") private Long projectId; //Network information @@ -72,7 +72,7 @@ public class AssignVMCmd extends BaseCmd { type = CommandType.LIST, collectionType = CommandType.UUID, entityType = NetworkResponse.class, - description = "list of new network ids in which the moved VM will participate. In case no network ids are provided the VM will be part of the default network for that zone. " + description = "List of new network IDs in which the moved Instance will participate. In case no network ids are provided the Instance will be part of the default network for that zone. " + "In case there is no network yet created for the new account the default network will be created.") private List networkIds; @@ -81,10 +81,13 @@ public class AssignVMCmd extends BaseCmd { type = CommandType.LIST, collectionType = CommandType.UUID, entityType = SecurityGroupResponse.class, - description = "list of security group ids to be applied on the virtual machine. " + - "In case no security groups are provided the VM is part of the default security group.") + description = "List of security group ids to be applied on the Instance. " + + "In case no security groups are provided the Instance is part of the default security group.") private List securityGroupIdList; + // Internal flag to allow assignment without adding a network + private boolean skipNetwork = false; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -113,6 +116,34 @@ public List getSecurityGroupIdList() { return securityGroupIdList; } + public boolean isSkipNetwork() { + return skipNetwork; + } + + ///////////////////////////////////////////////////// + /////////////////// Setters ///////////////////////// + ///////////////////////////////////////////////////// + + public void setVirtualMachineId(Long virtualMachineId) { + this.virtualMachineId = virtualMachineId; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public void setDomainId(Long domainId) { + this.domainId = domainId; + } + + public void setProjectId(Long projectId) { + this.projectId = projectId; + } + + public void setSkipNetwork(boolean skipNetwork) { + this.skipNetwork = skipNetwork; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -126,7 +157,7 @@ public void execute() { setResponseObject(response); } catch (Exception e) { ApiErrorCode errorCode = e instanceof InvalidParameterValueException ? ApiErrorCode.PARAM_ERROR : ApiErrorCode.INTERNAL_ERROR; - String msg = String.format("Failed to move VM [%s].", getVmId()); + String msg = String.format("Failed to move Instance due to [%s].", getVmId()); logger.error(msg, e); throw new ServerApiException(errorCode, msg); } 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 index d95f17ef304c..280e4a8bab77 100644 --- 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 @@ -45,6 +45,8 @@ public class CreateVMFromBackupCmdByAdmin extends CreateVMFromBackupCmd implemen @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; + private String instanceType; + public Long getPodId() { return podId; } @@ -52,4 +54,17 @@ public Long getPodId() { public Long getClusterId() { return clusterId; } + + @Override + public String getInstanceType() { + return instanceType; + } + + public CreateVMFromBackupCmdByAdmin(){} + + public CreateVMFromBackupCmdByAdmin(String hypervisor, String instanceType) { + this.displayVm = false; + this.hypervisor = hypervisor; + this.instanceType = instanceType; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/DeployVMCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/DeployVMCmdByAdmin.java index 6bb7657b86ba..b7c3b2ba41a6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/DeployVMCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/DeployVMCmdByAdmin.java @@ -30,17 +30,30 @@ import com.cloud.vm.VirtualMachine; -@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.Full, entityType = {VirtualMachine.class}, +@APICommand(name = "deployVirtualMachine", description = "Creates and automatically starts an Instance based on a service offering, disk offering, and Template.", responseObject = UserVmResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class DeployVMCmdByAdmin extends DeployVMCmd 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.13") + @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "Destination Pod ID to deploy the Instance to - parameter available for root admin only", since = "4.13") 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.13") + @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "Destination Cluster ID to deploy the Instance to - parameter available for root admin only", since = "4.13") private Long clusterId; + @Parameter(name = ApiConstants.BLANK_INSTANCE, + type = CommandType.BOOLEAN, + description = "Whether to create a blank instance without storage and network", + since = "4.23.0") + private Boolean blankInstance; + + // Internal flag to allow deploying instance with a given type + private String instanceType; + + ///////////////////////////////////////////////////// + ////////////////// Getters ////////////////////////// + ///////////////////////////////////////////////////// + public Long getPodId() { return podId; } @@ -48,4 +61,37 @@ public Long getPodId() { public Long getClusterId() { return clusterId; } + + @Override + public boolean isBlankInstance() { + return Boolean.TRUE.equals(blankInstance); + } + + @Override + public String getInstanceType() { + if (!isBlankInstance()) { + return null; + } + return instanceType; + } + + ///////////////////////////////////////////////////// + ////////////////// Setters ////////////////////////// + ///////////////////////////////////////////////////// + + public void setClusterId(Long clusterId) { + this.clusterId = clusterId; + } + + public void setBlankInstance(boolean blankInstance) { + this.blankInstance = blankInstance; + } + + public void setInstanceType(String instanceType) { + this.instanceType = instanceType; + } + + public void setCustomId(String customId) { + this.customId = customId; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/DestroyVMCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/DestroyVMCmdByAdmin.java index 08a13649bfa9..cbe6494d4004 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/DestroyVMCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/DestroyVMCmdByAdmin.java @@ -17,6 +17,8 @@ package org.apache.cloudstack.api.command.admin.vm; import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.command.admin.AdminCmd; import org.apache.cloudstack.api.command.user.vm.DestroyVMCmd; @@ -24,7 +26,23 @@ import com.cloud.vm.VirtualMachine; -@APICommand(name = "destroyVirtualMachine", description = "Destroys a virtual machine. Once destroyed, only the administrator can recover it.", responseObject = UserVmResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, +@APICommand(name = "destroyVirtualMachine", description = "Destroys an Instance. Once destroyed, only the administrator can recover it.", responseObject = UserVmResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) -public class DestroyVMCmdByAdmin extends DestroyVMCmd implements AdminCmd {} +public class DestroyVMCmdByAdmin extends DestroyVMCmd implements AdminCmd { + + @Parameter( name = ApiConstants.FORCED, + type = CommandType.BOOLEAN, + description = "Force destroy the Instance", + since = "4.23.0") + Boolean forced; + + @Override + public boolean isForced() { + return Boolean.TRUE.equals(forced); + } + + public void setForced(Boolean forced) { + this.forced = forced; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ExpungeVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ExpungeVMCmd.java index a964e873bad1..2d9c5f93383c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ExpungeVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ExpungeVMCmd.java @@ -39,7 +39,7 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VirtualMachine; -@APICommand(name = "expungeVirtualMachine", description = "Expunge a virtual machine. Once expunged, it cannot be recoverd.", responseObject = SuccessResponse.class, entityType = {VirtualMachine.class}, +@APICommand(name = "expungeVirtualMachine", description = "Expunge an Instance. Once expunged, it cannot be recovered.", responseObject = SuccessResponse.class, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ExpungeVMCmd extends BaseAsyncCmd { @@ -49,7 +49,7 @@ public class ExpungeVMCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, description = "The ID of the virtual machine") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, description = "The ID of the Instance") private Long id; ///////////////////////////////////////////////////// @@ -81,7 +81,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Expunging vm: " + this._uuidMgr.getUuid(VirtualMachine.class, getId()); + return "Expunging Instance with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -96,7 +96,7 @@ public Long getApiResourceId() { @Override public void execute() throws ResourceUnavailableException, ConcurrentOperationException { - CallContext.current().setEventDetails("Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getId())); + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.ID)); try { UserVm result = _userVmService.expungeVm(this.getId()); @@ -104,7 +104,7 @@ public void execute() throws ResourceUnavailableException, ConcurrentOperationEx SuccessResponse response = new SuccessResponse(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to expunge vm"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to expunge Instance"); } } catch (InvalidParameterValueException ipve) { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ipve.getMessage()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/GetVMUserDataCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/GetVMUserDataCmd.java index 8745ef12ce44..2f0aba43ad0e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/GetVMUserDataCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/GetVMUserDataCmd.java @@ -28,7 +28,7 @@ import com.cloud.user.Account; import com.cloud.uservm.UserVm; -@APICommand(name = "getVirtualMachineUserData", description = "Returns user data associated with the VM", responseObject = VMUserDataResponse.class, since = "4.4", +@APICommand(name = "getVirtualMachineUserData", description = "Returns user data associated with the Instance", responseObject = VMUserDataResponse.class, since = "4.4", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class GetVMUserDataCmd extends BaseCmd { @@ -36,7 +36,7 @@ public class GetVMUserDataCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, description = "The ID of the virtual machine") + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, description = "The ID of the Instance") private Long vmId; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportUnmanagedInstanceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportUnmanagedInstanceCmd.java index ae6ceff26c7d..3284dbafe7ca 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportUnmanagedInstanceCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportUnmanagedInstanceCmd.java @@ -56,7 +56,7 @@ import com.cloud.vm.VmDetailConstants; @APICommand(name = "importUnmanagedInstance", - description = "Import unmanaged virtual machine from a given cluster.", + description = "Import unmanaged Instance from a given cluster.", responseObject = UserVmResponse.class, responseView = ResponseObject.ResponseView.Full, requestHasSensitiveInfo = false, @@ -76,83 +76,83 @@ public class ImportUnmanagedInstanceCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = ClusterResponse.class, required = true, - description = "the cluster ID") + description = "The cluster ID") private Long clusterId; @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, - description = "the name of the instance as it is known to the hypervisor") + description = "The name of the Instance as it is known to the hypervisor") private String name; @Parameter(name = ApiConstants.DISPLAY_NAME, type = CommandType.STRING, - description = "the display name of the instance") + description = "The display name of the Instance") private String displayName; @Parameter(name = ApiConstants.HOST_NAME, type = CommandType.STRING, - description = "the host name of the instance") + description = "The host name of the Instance") private String hostName; @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, - description = "an optional account for the virtual machine. Must be used with domainId.") + description = "An optional account for the Instance. Must be used with domainId.") private String accountName; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "import instance to the domain specified") + description = "Import Instance to the domain specified") private Long domainId; @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, - description = "import instance for the project") + description = "Import Instance for the project") private Long projectId; @Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, - description = "the ID of the template for the virtual machine") + description = "The ID of the Template for the Instance") private Long templateId; @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, required = true, - description = "the service offering for the virtual machine") + description = "The service offering for the Instance") private Long serviceOfferingId; @Parameter(name = ApiConstants.NIC_NETWORK_LIST, type = CommandType.MAP, - description = "VM nic to network id mapping using keys nic and network") + description = "VM NIC to network id mapping using keys NIC and network") private Map nicNetworkList; @Parameter(name = ApiConstants.NIC_IP_ADDRESS_LIST, type = CommandType.MAP, - description = "VM nic to ip address mapping using keys nic, ip4Address") + description = "VM NIC to ip address mapping using keys NIC, ip4Address") private Map nicIpAddressList; @Parameter(name = ApiConstants.DATADISK_OFFERING_LIST, type = CommandType.MAP, - description = "datadisk template to disk-offering mapping using keys disk and diskOffering") + description = "Datadisk Template to disk-offering mapping using keys disk and diskOffering") private Map dataDiskToDiskOfferingList; @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, - description = "used to specify the custom parameters.") + description = "Used to specify the custom parameters.") private Map details; @Parameter(name = ApiConstants.MIGRATE_ALLOWED, type = CommandType.BOOLEAN, - description = "vm and its volumes are allowed to migrate to different host/pool when offerings passed are incompatible with current host/pool") + description = "Instance and its volumes are allowed to migrate to different host/pool when offerings passed are incompatible with current host/pool") private Boolean migrateAllowed; @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, - description = "VM is imported despite some of its NIC's MAC addresses are already present, in case the MAC address exists then a new MAC address is generated") + description = "Instance is imported despite some of its NIC's MAC addresses are already present, in case the MAC address exists then a new MAC address is generated") private Boolean forced; ///////////////////////////////////////////////////// @@ -201,9 +201,7 @@ public Map getNicNetworkList() { for (Map entry : (Collection>)nicNetworkList.values()) { String nic = entry.get(VmDetailConstants.NIC); String networkUuid = entry.get(VmDetailConstants.NETWORK); - if (logger.isDebugEnabled()) { - logger.debug(String.format("nic, '%s', goes on net, '%s'", nic, networkUuid)); - } + logger.debug("Checking if NIC '{}' can be mapped on network '{}'", nic, networkUuid); if (StringUtils.isAnyEmpty(nic, networkUuid) || _entityMgr.findByUuid(Network.class, networkUuid) == null) { throw new InvalidParameterValueException(String.format("Network ID: %s for NIC ID: %s is invalid", networkUuid, nic)); } @@ -219,9 +217,7 @@ public Map getNicIpAddressList() { for (Map entry : (Collection>)nicIpAddressList.values()) { String nic = entry.get(VmDetailConstants.NIC); String ipAddress = StringUtils.defaultIfEmpty(entry.get(VmDetailConstants.IP4_ADDRESS), null); - if (logger.isDebugEnabled()) { - logger.debug(String.format("nic, '%s', gets ip, '%s'", nic, ipAddress)); - } + logger.debug("Checking if IP address '{}' can be mapped to NIC '{}'", ipAddress, nic); if (StringUtils.isEmpty(nic)) { throw new InvalidParameterValueException(String.format("NIC ID: '%s' is invalid for IP address mapping", nic)); } @@ -244,9 +240,7 @@ public Map getDataDiskToDiskOfferingList() { for (Map entry : (Collection>)dataDiskToDiskOfferingList.values()) { String disk = entry.get(VmDetailConstants.DISK); String offeringUuid = entry.get(VmDetailConstants.DISK_OFFERING); - if (logger.isTraceEnabled()) { - logger.trace(String.format("disk, '%s', gets offering, '%s'", disk, offeringUuid)); - } + logger.trace("Checking if offering '{}' can be used on disk '{}'", offeringUuid, disk); if (StringUtils.isAnyEmpty(disk, offeringUuid) || _entityMgr.findByUuid(DiskOffering.class, offeringUuid) == null) { throw new InvalidParameterValueException(String.format("Disk offering ID: %s for disk ID: %s is invalid", offeringUuid, disk)); } @@ -277,8 +271,7 @@ public String getEventType() { @Override public String getEventDescription() { - String vmName = this.name; - return String.format("Importing unmanaged VM: %s", vmName); + return "Importing unmanaged Instance: " + name; } public boolean isForced() { @@ -298,7 +291,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { Account account = CallContext.current().getCallingAccount(); if (account != null) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportVmCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportVmCmd.java index db43b53ab9ab..db7dcc3fb44f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportVmCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportVmCmd.java @@ -30,6 +30,7 @@ import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuestOSResponse; import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.NetworkResponse; import org.apache.cloudstack.api.response.StoragePoolResponse; @@ -159,6 +160,33 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd { description = "(only for importing VMs from VMware to KVM) optional - if true, forces MS to export OVF from VMware to temporary storage, else uses KVM Host if ovftool is available, falls back to MS if not.") private Boolean forceMsToImportVmFiles; + @Parameter(name = ApiConstants.EXTRA_PARAMS, + type = CommandType.STRING, + since = "4.22", + description = "(only for importing VMs from VMware to KVM) optional - extra parameters to be passed on the virt-v2v command, if allowed by the administrator") + private String extraParams; + + @Parameter(name = ApiConstants.FORCE_CONVERT_TO_POOL, + type = CommandType.BOOLEAN, + since = "4.22", + description = "(only for importing VMs from VMware to KVM) optional - if true, forces virt-v2v conversions to write directly on the provided storage pool (avoid using temporary conversion pool).") + private Boolean forceConvertToPool; + + @Parameter(name = ApiConstants.OS_ID, + type = CommandType.UUID, + entityType = GuestOSResponse.class, + since = "4.22.1", + description = "(only for importing VMs from VMware to KVM) optional - the ID of the guest OS for the imported VM.") + private Long guestOsId; + + @Parameter(name = ApiConstants.USE_VDDK, + type = CommandType.BOOLEAN, + since = "4.22.1", + description = "(only for importing VMs from VMware to KVM) optional - if true, uses VDDK on the KVM conversion host for converting the VM. " + + "This parameter is mutually exclusive with " + ApiConstants.FORCE_MS_TO_IMPORT_VM_FILES + ".") + private Boolean useVddk; + + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -235,6 +263,10 @@ public Long getStoragePoolId() { return storagePoolId; } + public boolean getUseVddk() { + return BooleanUtils.toBooleanDefaultIfNull(useVddk, true); + } + public String getTmpPath() { return tmpPath; } @@ -248,6 +280,18 @@ public String getEventType() { return EventTypes.EVENT_VM_IMPORT; } + public String getExtraParams() { + return extraParams; + } + + public boolean getForceConvertToPool() { + return BooleanUtils.toBooleanDefaultIfNull(forceConvertToPool, false); + } + + public Long getGuestOsId() { + return guestOsId; + } + @Override public String getEventDescription() { String vmName = getName(); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ListImportVMTasksCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ListImportVMTasksCmd.java new file mode 100644 index 000000000000..94b547ff4267 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ListImportVMTasksCmd.java @@ -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. +package org.apache.cloudstack.api.command.admin.vm; + +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.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.ServerApiException; +import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.ImportVMTaskResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.vm.ImportVmTasksManager; + +import javax.inject.Inject; + +@APICommand(name = "listImportVmTasks", + description = "List running import virtual machine tasks from a unmanaged hosts into CloudStack", + responseObject = ImportVMTaskResponse.class, + responseView = ResponseObject.ResponseView.Full, + requestHasSensitiveInfo = false, + authorized = {RoleType.Admin}, + since = "4.22") +public class ListImportVMTasksCmd extends BaseListCmd { + + @Inject + public ImportVmTasksManager importVmTasksManager; + + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.UUID, + entityType = ZoneResponse.class, + required = true, + description = "the zone ID") + private Long zoneId; + + @Parameter(name = ApiConstants.ACCOUNT_ID, + type = CommandType.UUID, + entityType = AccountResponse.class, + description = "the ID of the Account") + private Long accountId; + + @Parameter(name = ApiConstants.VCENTER, + type = CommandType.STRING, + description = "The name/ip of vCenter. Make sure it is IP address or full qualified domain name for host running vCenter server.") + private String vcenter; + + @Parameter(name = ApiConstants.CONVERT_INSTANCE_HOST_ID, + type = CommandType.UUID, + entityType = HostResponse.class, + description = "Conversion host of the importing task") + private Long convertHostId; + + @Parameter(name = ApiConstants.TASKS_FILTER, type = CommandType.STRING, description = "Filter tasks by state, valid options are: All, Running, Completed, Failed") + private String tasksFilter; + + public Long getZoneId() { + return zoneId; + } + + public Long getAccountId() { + return accountId; + } + + public String getVcenter() { + return vcenter; + } + + public Long getConvertHostId() { + return convertHostId; + } + + public String getTasksFilter() { + return tasksFilter; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + ListResponse response = importVmTasksManager.listImportVMTasks(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + Account account = CallContext.current().getCallingAccount(); + if (account != null) { + return account.getId(); + } + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ListUnmanagedInstancesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ListUnmanagedInstancesCmd.java index 6932aa383fa2..1d6285684471 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ListUnmanagedInstancesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ListUnmanagedInstancesCmd.java @@ -41,7 +41,7 @@ import com.cloud.user.Account; @APICommand(name = "listUnmanagedInstances", - description = "Lists unmanaged virtual machines for a given cluster.", + description = "Lists unmanaged Instances for a given cluster.", responseObject = UnmanagedInstanceResponse.class, responseView = ResponseObject.ResponseView.Full, entityType = {UnmanagedInstanceTO.class}, @@ -62,12 +62,12 @@ public class ListUnmanagedInstancesCmd extends BaseListCmd { type = CommandType.UUID, entityType = ClusterResponse.class, required = true, - description = "the cluster ID") + description = "The cluster ID") private Long clusterId; @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, - description = "the hypervisor name of the instance") + description = "The hypervisor name of the Instance") private String name; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ListVMsCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ListVMsCmdByAdmin.java index b48941e7d17a..52309855a7a2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ListVMsCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ListVMsCmdByAdmin.java @@ -30,7 +30,7 @@ import com.cloud.vm.VirtualMachine; -@APICommand(name = "listVirtualMachines", description = "List the virtual machines owned by the account.", responseObject = UserVmResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, +@APICommand(name = "listVirtualMachines", description = "List the Instances owned by the account.", responseObject = UserVmResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class ListVMsCmdByAdmin extends ListVMsCmd implements AdminCmd { @@ -39,19 +39,19 @@ public class ListVMsCmdByAdmin extends ListVMsCmd implements AdminCmd { ///////////////////////////////////////////////////// @Parameter(name=ApiConstants.HOST_ID, type=CommandType.UUID, entityType=HostResponse.class, - description="the host ID") + description = "The host ID") private Long hostId; @Parameter(name=ApiConstants.POD_ID, type=CommandType.UUID, entityType=PodResponse.class, - description="the pod ID") + description = "The pod ID") private Long podId; @Parameter(name=ApiConstants.STORAGE_ID, type=CommandType.UUID, entityType=StoragePoolResponse.class, - description="the storage ID where vm's volumes belong to") + description = "The storage ID where Instance's volumes belong to") private Long storageId; @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, - description = "the cluster ID", since = "4.16.0") + description = "The cluster ID", since = "4.16.0") private Long 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 0bc993ef1f71..467b92d415d2 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 @@ -45,7 +45,7 @@ import com.cloud.vm.VirtualMachine; @APICommand(name = "migrateVirtualMachine", - description = "Attempts Migration of a VM to a different host or Root volume of the vm to a different storage pool", + description = "Attempts Migration of an Instance to a different host or Root volume of the Instance to a different storage pool", responseObject = UserVmResponse.class, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) @@ -60,21 +60,21 @@ public class MigrateVMCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = HostResponse.class, required = false, - description = "Destination Host ID to migrate VM to.") + description = "Destination Host ID to migrate Instance to.") private Long hostId; @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, - description = "the ID of the virtual machine") + description = "The ID of the Instance") private Long virtualMachineId; @Parameter(name = ApiConstants.STORAGE_ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, required = false, - description = "Destination storage pool ID to migrate VM volumes to. Required for migrating the root disk volume") + description = "Destination storage pool ID to migrate Instance volumes to. Required for migrating the root disk volume") private Long storageId; @Parameter(name = ApiConstants.AUTO_SELECT, @@ -124,15 +124,15 @@ public String getEventType() { @Override public String getEventDescription() { - String eventDescription; + String description = "Attempting to migrate Instance with ID: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID); + if (getHostId() != null) { - eventDescription = String.format("Attempting to migrate VM id: %s to host Id: %s", getVirtualMachineId(), getHostId()); + description += " to host with ID: " +getResourceUuid(ApiConstants.HOST_ID); } else if (getStoragePoolId() != null) { - eventDescription = String.format("Attempting to migrate VM id: %s to storage pool Id: %s", getVirtualMachineId(), getStoragePoolId()); - } else { - eventDescription = String.format("Attempting to migrate VM id: %s", getVirtualMachineId()); + description = " to storage pool with ID: " + getResourceUuid(ApiConstants.STORAGE_ID); } - return eventDescription; + + return description; } @Override @@ -143,7 +143,7 @@ public void execute() { UserVm userVm = _userVmService.getUserVm(getVirtualMachineId()); if (userVm == null) { - throw new InvalidParameterValueException("Unable to find the VM by id=" + getVirtualMachineId()); + throw new InvalidParameterValueException("Unable to find the Instance by id=" + getVirtualMachineId()); } if (Hypervisor.HypervisorType.External.equals(userVm.getHypervisorType())) { @@ -156,18 +156,18 @@ public void execute() { if (getStoragePoolId() != null) { destStoragePool = _storageService.getStoragePool(getStoragePoolId()); if (destStoragePool == null) { - throw new InvalidParameterValueException("Unable to find the storage pool to migrate the VM"); + throw new InvalidParameterValueException("Unable to find the storage pool to migrate the Instance"); } - CallContext.current().setEventDetails("VM Id: " + getVirtualMachineId() + " to storage pool Id: " + getStoragePoolId()); + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID) + " to storage pool with ID: " + getResourceUuid(ApiConstants.STORAGE_ID)); } else if (getHostId() != null) { destinationHost = _resourceService.getHost(getHostId()); if (destinationHost == null) { - throw new InvalidParameterValueException("Unable to find the host to migrate the VM, host id=" + getHostId()); + throw new InvalidParameterValueException("Unable to find the host to migrate the Instance, host id=" + getHostId()); } if (destinationHost.getType() != Host.Type.Routing) { - throw new InvalidParameterValueException("The specified host(" + destinationHost.getName() + ") is not suitable to migrate the VM, please specify another one"); + throw new InvalidParameterValueException("The specified host(" + destinationHost.getName() + ") is not suitable to migrate the Instance, please specify another one"); } - CallContext.current().setEventDetails("VM Id: " + getVirtualMachineId() + " to host Id: " + getHostId()); + CallContext.current().setEventDetails("Instance Id: " + getVirtualMachineId() + " to host with ID: " + getResourceUuid(ApiConstants.HOST_ID)); } else if (! isAutoSelect()) { throw new InvalidParameterValueException("Please specify a host or storage as destination, or pass 'autoselect=true' to automatically select a destination host which do not require storage migration"); } @@ -184,7 +184,7 @@ public void execute() { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to migrate vm"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to migrate Instance"); } } catch (ResourceUnavailableException ex) { logger.warn("Exception: ", ex); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmd.java index b736e8606364..ad298513ac0b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmd.java @@ -46,7 +46,7 @@ import com.cloud.vm.VirtualMachine; @APICommand(name = "migrateVirtualMachineWithVolume", - description = "Attempts Migration of a VM with its volumes to a different host", + description = "Attempts Migration of an Instance with its volumes to a different host", responseObject = UserVmResponse.class, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) @@ -60,14 +60,14 @@ public class MigrateVirtualMachineWithVolumeCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, - description = "Destination Host ID to migrate VM to.") + description = "Destination Host ID to migrate Instance to.") private Long hostId; @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, - description = "the ID of the virtual machine") + description = "The ID of the Instance") private Long virtualMachineId; @Parameter(name = ApiConstants.MIGRATE_TO, @@ -84,7 +84,7 @@ public class MigrateVirtualMachineWithVolumeCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.AUTO_SELECT, since = "4.19.0", type = CommandType.BOOLEAN, - description = "Automatically select a destination host for a running instance, if hostId is not specified. false by default") + description = "Automatically select a destination host for a running Instance, if hostId is not specified. false by default") private Boolean autoSelect; ///////////////////////////////////////////////////// @@ -135,7 +135,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Attempting to migrate VM Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getVirtualMachineId()) + " to host Id: " + this._uuidMgr.getUuid(Host.class, getHostId()); + return "Attempting to migrate Instance with ID: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID) + " to host with ID: " + getResourceUuid(ApiConstants.HOST_ID); } @Override @@ -155,8 +155,8 @@ private Host getDestinationHost() { Host destinationHost = _resourceService.getHost(getHostId()); // OfflineVmwareMigration: destination host would have to not be a required parameter for stopped VMs if (destinationHost == null) { - logger.error(String.format("Unable to find the host with ID [%s].", getHostId())); - throw new InvalidParameterValueException("Unable to find the specified host to migrate the VM."); + logger.error("Unable to find the host with ID [{}].", getHostId()); + throw new InvalidParameterValueException("Unable to find the specified host to migrate the Instance."); } return destinationHost; } @@ -164,7 +164,7 @@ private Host getDestinationHost() { @Override public void execute() { if (hostId == null && MapUtils.isEmpty(migrateVolumeTo) && !Boolean.TRUE.equals(autoSelect)) { - throw new InvalidParameterValueException(String.format("Either %s or %s must be passed or %s must be true for migrating the VM.", ApiConstants.HOST_ID, ApiConstants.MIGRATE_TO, ApiConstants.AUTO_SELECT)); + throw new InvalidParameterValueException(String.format("Either %s or %s must be passed or %s must be true for migrating the Instance.", ApiConstants.HOST_ID, ApiConstants.MIGRATE_TO, ApiConstants.AUTO_SELECT)); } VirtualMachine virtualMachine = _userVmService.getVm(getVirtualMachineId()); @@ -188,7 +188,7 @@ public void execute() { if (migratedVm != null) { setResponseBasedOnVmType(virtualMachine, migratedVm); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to migrate vm"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to migrate Instance"); } } catch (ResourceUnavailableException ex) { logger.warn("Exception: ", ex); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/RebootVMCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/RebootVMCmdByAdmin.java index 5f6a7ab384be..36fdc9d8ec18 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/RebootVMCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/RebootVMCmdByAdmin.java @@ -24,6 +24,6 @@ import com.cloud.vm.VirtualMachine; -@APICommand(name = "rebootVirtualMachine", description = "Reboots a virtual machine.", responseObject = UserVmResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, +@APICommand(name = "rebootVirtualMachine", description = "Reboots an Instance.", responseObject = UserVmResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class RebootVMCmdByAdmin extends RebootVMCmd implements AdminCmd {} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/RecoverVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/RecoverVMCmd.java index f34d555dc707..4f63e22b8677 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/RecoverVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/RecoverVMCmd.java @@ -34,7 +34,7 @@ import com.cloud.uservm.UserVm; import com.cloud.vm.VirtualMachine; -@APICommand(name = "recoverVirtualMachine", description = "Recovers a virtual machine.", responseObject = UserVmResponse.class, entityType = {VirtualMachine.class}, +@APICommand(name = "recoverVirtualMachine", description = "Recovers an Instance.", responseObject = UserVmResponse.class, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class RecoverVMCmd extends BaseCmd { @@ -44,7 +44,7 @@ public class RecoverVMCmd extends BaseCmd { ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, description = "The ID of the virtual machine") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, description = "The ID of the Instance") private Long id; ///////////////////////////////////////////////////// @@ -87,7 +87,7 @@ public void execute() throws ResourceAllocationException { recoverVmResponse.setResponseName(getCommandName()); setResponseObject(recoverVmResponse); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to recover vm"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to recover Instance"); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/RemoveNicFromVMCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/RemoveNicFromVMCmdByAdmin.java index 89726afe99b5..e25c850e1278 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/RemoveNicFromVMCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/RemoveNicFromVMCmdByAdmin.java @@ -24,6 +24,6 @@ import com.cloud.vm.VirtualMachine; -@APICommand(name = "removeNicFromVirtualMachine", description = "Removes VM from specified network by deleting a NIC", responseObject = UserVmResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, +@APICommand(name = "removeNicFromVirtualMachine", description = "Removes Instance from specified network by deleting a NIC", responseObject = UserVmResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class RemoveNicFromVMCmdByAdmin extends RemoveNicFromVMCmd implements AdminCmd {} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ResetVMPasswordCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ResetVMPasswordCmdByAdmin.java index d7ab0c61bbbc..1b4af79d775d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ResetVMPasswordCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ResetVMPasswordCmdByAdmin.java @@ -24,8 +24,8 @@ import com.cloud.vm.VirtualMachine; -@APICommand(name = "resetPasswordForVirtualMachine", responseObject=UserVmResponse.class, description="Resets the password for virtual machine. " + - "The virtual machine must be in a \"Stopped\" state and the template must already " + +@APICommand(name = "resetPasswordForVirtualMachine", responseObject=UserVmResponse.class, description = "Resets the password for Instance. " + + "The Instance must be in a \"Stopped\" state and the Template must already " + "support this feature for this command to take effect. [async]", responseView = ResponseView.Full, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class ResetVMPasswordCmdByAdmin extends ResetVMPasswordCmd implements AdminCmd {} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ResetVMSSHKeyCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ResetVMSSHKeyCmdByAdmin.java index ed9cc11fd5e4..046a90892da6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ResetVMSSHKeyCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ResetVMSSHKeyCmdByAdmin.java @@ -25,7 +25,7 @@ import com.cloud.vm.VirtualMachine; -@APICommand(name = "resetSSHKeyForVirtualMachine", responseObject = UserVmResponse.class, description = "Resets the SSH Key for virtual machine. " + - "The virtual machine must be in a \"Stopped\" state. [async]", responseView = ResponseView.Full, entityType = {VirtualMachine.class}, +@APICommand(name = "resetSSHKeyForVirtualMachine", responseObject = UserVmResponse.class, description = "Resets the SSH Key for Instance. " + + "The Instance must be in a \"Stopped\" state. [async]", responseView = ResponseView.Full, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class ResetVMSSHKeyCmdByAdmin extends ResetVMSSHKeyCmd implements AdminCmd {} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ResetVMUserDataCmdAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ResetVMUserDataCmdAdmin.java index 83e3481840b9..6ae9cb837e56 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ResetVMUserDataCmdAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ResetVMUserDataCmdAdmin.java @@ -23,8 +23,8 @@ import org.apache.cloudstack.api.command.user.vm.ResetVMUserDataCmd; import org.apache.cloudstack.api.response.UserVmResponse; -@APICommand(name = "resetUserDataForVirtualMachine", responseObject = UserVmResponse.class, description = "Resets the UserData for virtual machine. " + - "The virtual machine must be in a \"Stopped\" state. [async]", responseView = ResponseObject.ResponseView.Full, entityType = {VirtualMachine.class}, +@APICommand(name = "resetUserDataForVirtualMachine", responseObject = UserVmResponse.class, description = "Resets the UserData for Instance. " + + "The Instance must be in a \"Stopped\" state. [async]", responseView = ResponseObject.ResponseView.Full, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class ResetVMUserDataCmdAdmin extends ResetVMUserDataCmd implements AdminCmd { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/RestoreVMCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/RestoreVMCmdByAdmin.java index b3ee39829ab0..9a551653e628 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/RestoreVMCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/RestoreVMCmdByAdmin.java @@ -24,7 +24,7 @@ import com.cloud.vm.VirtualMachine; -@APICommand(name = "restoreVirtualMachine", description = "Restore a VM to original template/ISO or new template/ISO", responseObject = UserVmResponse.class, since = "3.0.0", responseView = ResponseView.Full, entityType = {VirtualMachine.class}, +@APICommand(name = "restoreVirtualMachine", description = "Restore an Instance to original Template/ISO or new Template/ISO", responseObject = UserVmResponse.class, since = "3.0.0", responseView = ResponseView.Full, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class RestoreVMCmdByAdmin extends RestoreVMCmd implements AdminCmd {} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ScaleVMCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ScaleVMCmdByAdmin.java index fd71a4459540..6ed7a0cca8d6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ScaleVMCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ScaleVMCmdByAdmin.java @@ -25,6 +25,6 @@ import com.cloud.vm.VirtualMachine; -@APICommand(name = "scaleVirtualMachine", description = "Scales the virtual machine to a new service offering. This command also considers the volume size in the service offering or disk offering linked to the new service offering and apply all characteristics to the root volume.", responseObject = SuccessResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, +@APICommand(name = "scaleVirtualMachine", description = "Scales the Instance to a new service offering. This command also considers the volume size in the service offering or disk offering linked to the new service offering and apply all characteristics to the root volume.", responseObject = SuccessResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ScaleVMCmdByAdmin extends ScaleVMCmd implements AdminCmd {} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/StartVMCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/StartVMCmdByAdmin.java index f87622cf734a..30fab3b9382f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/StartVMCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/StartVMCmdByAdmin.java @@ -24,6 +24,6 @@ import com.cloud.vm.VirtualMachine; -@APICommand(name = "startVirtualMachine", responseObject = UserVmResponse.class, description = "Starts a virtual machine.", responseView = ResponseView.Full, entityType = {VirtualMachine.class}, +@APICommand(name = "startVirtualMachine", responseObject = UserVmResponse.class, description = "Starts an Instance.", responseView = ResponseView.Full, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class StartVMCmdByAdmin extends StartVMCmd implements AdminCmd {} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/StopVMCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/StopVMCmdByAdmin.java index 2f7cc2198ef7..6dc1947712d6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/StopVMCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/StopVMCmdByAdmin.java @@ -24,6 +24,6 @@ import com.cloud.vm.VirtualMachine; -@APICommand(name = "stopVirtualMachine", responseObject = UserVmResponse.class, description = "Stops a virtual machine.", responseView = ResponseView.Full, entityType = {VirtualMachine.class}, +@APICommand(name = "stopVirtualMachine", responseObject = UserVmResponse.class, description = "Stops an Instance.", responseView = ResponseView.Full, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class StopVMCmdByAdmin extends StopVMCmd implements AdminCmd {} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/UnmanageVMInstanceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/UnmanageVMInstanceCmd.java index bbcb8840f666..2c9f09dcd626 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/UnmanageVMInstanceCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/UnmanageVMInstanceCmd.java @@ -27,6 +27,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.user.Account; import com.cloud.uservm.UserVm; +import com.cloud.utils.Pair; import com.cloud.vm.VirtualMachine; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; @@ -36,15 +37,17 @@ import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.UnmanageVMInstanceResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.vm.UnmanagedVMsManager; +import org.apache.commons.lang3.BooleanUtils; import javax.inject.Inject; @APICommand(name = "unmanageVirtualMachine", - description = "Unmanage a guest virtual machine.", + description = "Unmanage a Guest Instance.", entityType = {VirtualMachine.class}, responseObject = UnmanageVMInstanceResponse.class, requestHasSensitiveInfo = false, @@ -62,9 +65,23 @@ public class UnmanageVMInstanceCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, - description = "The ID of the virtual machine to unmanage") + description = "The ID of the Instance to unmanage") private Long vmId; + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, + entityType = HostResponse.class, required = false, + description = "ID of the host which will be used for unmanaging the Instance. " + + "Applicable only for KVM hypervisor and stopped Instances. Domain XML will be stored on this host.", + since = "4.22.0") + private Long hostId; + + @Parameter(name = ApiConstants.FORCED, + type = CommandType.BOOLEAN, + required = false, + description = "Force unmanaging Instance with config drive. Applicable only for KVM Hypervisor.", + since = "4.22.0") + private Boolean forced; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -80,7 +97,19 @@ public String getEventType() { @Override public String getEventDescription() { - return "unmanaging VM. VM ID = " + vmId; + return "Unmanaging Instance with ID: " + getResourceUuid(ApiConstants.ID); + } + + public Long getHostId() { + return hostId; + } + + public void setHostId(Long hostId) { + this.hostId = hostId; + } + + public Boolean isForced() { + return BooleanUtils.isTrue(forced); } ///////////////////////////////////////////////////// @@ -92,10 +121,11 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { UnmanageVMInstanceResponse response = new UnmanageVMInstanceResponse(); try { - CallContext.current().setEventDetails("VM ID = " + vmId); - boolean result = unmanagedVMsManager.unmanageVMInstance(vmId); - response.setSuccess(result); - if (result) { + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.ID)); + Pair result = unmanagedVMsManager.unmanageVMInstance(vmId, hostId, isForced()); + if (result.first()) { + response.setSuccess(true); + response.setHostId(result.second()); response.setDetails("VM unmanaged successfully"); } } catch (Exception e) { @@ -124,5 +154,4 @@ public ApiCommandResourceType getApiResourceType() { public Long getApiResourceId() { return vmId; } - } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/UpdateDefaultNicForVMCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/UpdateDefaultNicForVMCmdByAdmin.java index 1c4dde93a226..a702eb264c80 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/UpdateDefaultNicForVMCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/UpdateDefaultNicForVMCmdByAdmin.java @@ -24,6 +24,6 @@ import com.cloud.vm.VirtualMachine; -@APICommand(name = "updateDefaultNicForVirtualMachine", description = "Changes the default NIC on a VM", responseObject = UserVmResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, +@APICommand(name = "updateDefaultNicForVirtualMachine", description = "Changes the default NIC on an Instance", responseObject = UserVmResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class UpdateDefaultNicForVMCmdByAdmin extends UpdateDefaultNicForVMCmd implements AdminCmd {} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/UpdateVMCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/UpdateVMCmdByAdmin.java index cb4bb0433d63..46f5824de252 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/UpdateVMCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/UpdateVMCmdByAdmin.java @@ -25,8 +25,8 @@ import com.cloud.vm.VirtualMachine; -@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. " + - "Therefore, stop the VM manually before issuing this call.", responseObject = UserVmResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, +@APICommand(name = "updateVirtualMachine", description = "Updates properties of an Instance. The Instance has to be stopped and restarted for the " + + "new properties to take effect. UpdateVirtualMachine does not first check whether the Instance is stopped. " + + "Therefore, stop the Instance manually before issuing this call.", responseObject = UserVmResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class UpdateVMCmdByAdmin extends UpdateVMCmd implements AdminCmd {} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/UpgradeVMCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/UpgradeVMCmdByAdmin.java index f3230e64818c..9bf8ccb454b6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/UpgradeVMCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/UpgradeVMCmdByAdmin.java @@ -25,8 +25,8 @@ import com.cloud.vm.VirtualMachine; @Deprecated(since = "4.18") -@APICommand(name = "changeServiceForVirtualMachine", responseObject=UserVmResponse.class, description="(This API is deprecated, use scaleVirtualMachine API)" + - "Changes the service offering for a virtual machine. The virtual machine must be in a \"Stopped\" state for " + +@APICommand(name = "changeServiceForVirtualMachine", responseObject=UserVmResponse.class, description = "(This API is deprecated, use scaleVirtualMachine API)" + + "Changes the service offering for an Instance. The Instance must be in a \"Stopped\" state for " + "this command to take effect.", responseView = ResponseView.Full, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vmsnapshot/RevertToVMSnapshotCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vmsnapshot/RevertToVMSnapshotCmdByAdmin.java index 8f286626b992..a57362d62056 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vmsnapshot/RevertToVMSnapshotCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vmsnapshot/RevertToVMSnapshotCmdByAdmin.java @@ -22,6 +22,6 @@ import org.apache.cloudstack.api.command.user.vmsnapshot.RevertToVMSnapshotCmd; import org.apache.cloudstack.api.response.UserVmResponse; -@APICommand(name = "revertToVMSnapshot", description = "Revert VM from a vmsnapshot.", responseObject = UserVmResponse.class, since = "4.2.0", responseView = ResponseView.Full, +@APICommand(name = "revertToVMSnapshot", description = "Revert Instance from a vmsnapshot.", responseObject = UserVmResponse.class, since = "4.2.0", responseView = ResponseView.Full, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class RevertToVMSnapshotCmdByAdmin extends RevertToVMSnapshotCmd implements AdminCmd {} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/AttachVolumeCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/AttachVolumeCmdByAdmin.java index 6f31df79e1c9..20418d589405 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/AttachVolumeCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/AttachVolumeCmdByAdmin.java @@ -24,6 +24,6 @@ import com.cloud.vm.VirtualMachine; -@APICommand(name = "attachVolume", description = "Attaches a disk volume to a virtual machine.", responseObject = VolumeResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, +@APICommand(name = "attachVolume", description = "Attaches a disk volume to an Instance.", responseObject = VolumeResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class AttachVolumeCmdByAdmin extends AttachVolumeCmd implements AdminCmd {} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/CreateVolumeCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/CreateVolumeCmdByAdmin.java index c0dfe42acb5f..e156475b29ae 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/CreateVolumeCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/CreateVolumeCmdByAdmin.java @@ -25,7 +25,7 @@ import com.cloud.storage.Volume; import com.cloud.vm.VirtualMachine; -@APICommand(name = "createVolume", responseObject = VolumeResponse.class, description = "Creates a disk volume from a disk offering. This disk volume must still be attached to a virtual machine to make use of it.", responseView = ResponseView.Full, entityType = { +@APICommand(name = "createVolume", responseObject = VolumeResponse.class, description = "Creates a disk volume from a disk offering. This disk volume must still be attached to an Instance to make use of it.", responseView = ResponseView.Full, entityType = { Volume.class, VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateVolumeCmdByAdmin extends CreateVolumeCmd implements AdminCmd {} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/DestroyVolumeCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/DestroyVolumeCmdByAdmin.java index 0840b4ce6f99..de90fee102de 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/DestroyVolumeCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/DestroyVolumeCmdByAdmin.java @@ -40,7 +40,7 @@ public class DestroyVolumeCmdByAdmin extends DestroyVolumeCmd implements AdminCm @Override public void execute() { CallContext.current().setEventDetails("Volume Id: " + getId()); - Volume result = _volumeService.destroyVolume(getId(), CallContext.current().getCallingAccount(), getExpunge(), false); + Volume result = _volumeService.destroyVolume(getId(), CallContext.current().getCallingAccount(), getExpunge(), false, null); if (result != null) { VolumeResponse response = _responseGenerator.createVolumeResponse(ResponseView.Full, result); response.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/DetachVolumeCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/DetachVolumeCmdByAdmin.java index 36a183bc89fc..05f9fe6a31a6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/DetachVolumeCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/DetachVolumeCmdByAdmin.java @@ -24,6 +24,6 @@ import com.cloud.vm.VirtualMachine; -@APICommand(name = "detachVolume", description = "Detaches a disk volume from a virtual machine.", responseObject = VolumeResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, +@APICommand(name = "detachVolume", description = "Detaches a disk volume from an Instance.", responseObject = VolumeResponse.class, responseView = ResponseView.Full, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DetachVolumeCmdByAdmin extends DetachVolumeCmd implements AdminCmd {} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/ImportVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/ImportVolumeCmd.java index 57c3ee586d35..50f4b9c1fbe5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/ImportVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/ImportVolumeCmd.java @@ -156,7 +156,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/RecoverVolumeCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/RecoverVolumeCmdByAdmin.java index e276c8a00b65..b7c084ee6e78 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/RecoverVolumeCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/RecoverVolumeCmdByAdmin.java @@ -19,6 +19,7 @@ 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.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; @@ -38,7 +39,7 @@ public class RecoverVolumeCmdByAdmin extends RecoverVolumeCmd implements AdminCm @Override public void execute() { - CallContext.current().setEventDetails("Volume Id: " + getId()); + CallContext.current().setEventDetails("Volume ID: " + getResourceUuid(ApiConstants.ID)); Volume result = _volumeService.recoverVolume(getId()); if (result != null) { VolumeResponse response = _responseGenerator.createVolumeResponse(ResponseView.Full, result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/UnmanageVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/UnmanageVolumeCmd.java index dcc8b2af8d74..ac573dd4ecb9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/UnmanageVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/UnmanageVolumeCmd.java @@ -81,7 +81,7 @@ public String getEventType() { @Override public String getEventDescription() { - return String.format("Unmanaging Volume with ID %s", volumeId); + return "Unmanaging Volume with ID: " + getResourceUuid(ApiConstants.ID); } ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CloneVPCOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CloneVPCOfferingCmd.java new file mode 100644 index 000000000000..2148ff5c2d4f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CloneVPCOfferingCmd.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.vpc; + +import com.cloud.exception.ResourceAllocationException; +import com.cloud.network.vpc.VpcOffering; +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.VpcOfferingResponse; + +import java.util.List; + +@APICommand(name = "cloneVPCOffering", + description = "Clones an existing VPC offering. All parameters are copied from the source offering unless explicitly overridden. " + + "Use 'addServices' and 'dropServices' to modify the service list without respecifying everything.", + responseObject = VpcOfferingResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + since = "4.23.0") +public class CloneVPCOfferingCmd extends CreateVPCOfferingCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.SOURCE_OFFERING_ID, + type = BaseCmd.CommandType.UUID, + entityType = VpcOfferingResponse.class, + required = true, + description = "The ID of the source VPC offering to clone from") + private Long sourceOfferingId; + + @Parameter(name = "addservices", + type = CommandType.LIST, + collectionType = CommandType.STRING, + description = "Services to add to the cloned offering (in addition to source offering services). " + + "If specified along with 'supportedservices', this parameter is ignored.") + private List addServices; + + @Parameter(name = "dropservices", + type = CommandType.LIST, + collectionType = CommandType.STRING, + description = "Services to remove from the cloned offering (that exist in source offering). " + + "If specified along with 'supportedservices', this parameter is ignored.") + private List dropServices; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getSourceOfferingId() { + return sourceOfferingId; + } + + public List getAddServices() { + return addServices; + } + + public List getDropServices() { + return dropServices; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void create() throws ResourceAllocationException { + // Set a temporary entity ID (source offering ID) to prevent NullPointerException + // in ApiServer.queueCommand(). This will be updated in execute() with the actual + // cloned offering ID. + if (sourceOfferingId != null) { + setEntityId(sourceOfferingId); + } + } + + @Override + public void execute() { + VpcOffering result = _vpcProvSvc.cloneVPCOffering(this); + if (result != null) { + setEntityId(result.getId()); + setEntityUuid(result.getUuid()); + + VpcOfferingResponse response = _responseGenerator.createVpcOfferingResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to clone VPC offering"); + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreatePrivateGatewayByAdminCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreatePrivateGatewayByAdminCmd.java index 1b2163853ec3..09f0d21d9dfd 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreatePrivateGatewayByAdminCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreatePrivateGatewayByAdminCmd.java @@ -44,13 +44,13 @@ public class CreatePrivateGatewayByAdminCmd extends CreatePrivateGatewayCmd impl @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, - description = "the Physical Network ID the network belongs to") + description = "The Physical Network ID the network belongs to") private Long physicalNetworkId; - @Parameter(name = ApiConstants.VLAN, type = CommandType.STRING, description = "the network implementation uri for the private gateway") + @Parameter(name = ApiConstants.VLAN, type = CommandType.STRING, description = "The network implementation uri for the private gateway") private String broadcastUri; - @Parameter(name = ApiConstants.BYPASS_VLAN_OVERLAP_CHECK, type = CommandType.BOOLEAN, description = "when true bypasses VLAN id/range overlap check during private gateway creation") + @Parameter(name = ApiConstants.BYPASS_VLAN_OVERLAP_CHECK, type = CommandType.BOOLEAN, description = "When true bypasses VLAN id/range overlap check during private gateway creation") private Boolean bypassVlanOverlapCheck; ///////////////////////////////////////////////////// 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 84f4d4514604..2b934a60da7a 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 @@ -28,7 +28,6 @@ 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; @@ -62,6 +61,10 @@ 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) public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { @@ -70,49 +73,49 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the vpc offering") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "The name of the VPC offering") private String vpcOfferingName; - @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "the display text of the vpc offering, defaults to the 'name'") + @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "The display text of the VPC offering, defaults to the 'name'") private String displayText; @Parameter(name = ApiConstants.SUPPORTED_SERVICES, type = CommandType.LIST, collectionType = CommandType.STRING, - description = "services supported by the vpc offering") + description = "Services supported by the VPC offering") private List supportedServices; - @Parameter(name = ApiConstants.SERVICE_PROVIDER_LIST, type = CommandType.MAP, description = "provider to service mapping. " + @Parameter(name = ApiConstants.SERVICE_PROVIDER_LIST, type = CommandType.MAP, description = "Provider to service mapping. " + "If not specified, the provider for the service will be mapped to the default provider on the physical network") private Map> serviceProviderList; - @Parameter(name = ApiConstants.SERVICE_CAPABILITY_LIST, type = CommandType.MAP, description = "desired service capabilities as part of vpc offering", since = "4.4") + @Parameter(name = ApiConstants.SERVICE_CAPABILITY_LIST, type = CommandType.MAP, description = "Desired service capabilities as part of VPC offering", since = "4.4") private Map> serviceCapabilityList; @Parameter(name = ApiConstants.INTERNET_PROTOCOL, type = CommandType.STRING, - description = "The internet protocol of the offering. Options are ipv4 and dualstack. Default is ipv4. dualstack will create an offering that supports both IPv4 and IPv6", + description = "The internet protocol of the offering. Options are IPv4 and dualstack. Default is IPv4. dualstack will create an offering that supports both IPv4 and IPv6", since = "4.17.0") private String internetProtocol; @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, - description = "the ID of the service offering for the VPC router appliance") + description = "The ID of the service offering for the VPC router appliance") private Long serviceOfferingId; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = DomainResponse.class, - description = "the ID of the containing domain(s), null for public offerings") + description = "The ID of the containing domain(s), null for public offerings") private List domainIds; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = ZoneResponse.class, - description = "the ID of the containing zone(s), null for public offerings", + description = "The ID of the containing zone(s), null for public offerings", since = "4.13") private List zoneIds; @@ -137,7 +140,7 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { @Parameter(name = ApiConstants.ENABLE, type = CommandType.BOOLEAN, - description = "set to true if the offering is to be enabled during creation. Default is false", + description = "Set to true if the offering is to be enabled during creation. Default is false", since = "4.16") private Boolean enable; @@ -157,6 +160,12 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { description = "the routing mode for the VPC offering. Supported types are: Static or Dynamic.") private String routingMode; + @Parameter(name = ApiConstants.CONSERVE_MODE, type = CommandType.BOOLEAN, + since = "4.23.0", + description = "True if the VPC offering is IP conserve mode enabled, allowing public IPs to be used across multiple VPC tiers. Default value is false") + private Boolean conserveMode; + + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -175,9 +184,7 @@ public boolean isExternalNetworkProvider() { } public List getSupportedServices() { - if (!isExternalNetworkProvider() && CollectionUtils.isEmpty(supportedServices)) { - throw new InvalidParameterValueException("Supported services needs to be provided"); - } + // For external network providers, auto-populate services based on network mode if (isExternalNetworkProvider()) { supportedServices = new ArrayList<>(List.of( Dhcp.getName(), @@ -194,7 +201,7 @@ public List getSupportedServices() { if (NetworkOffering.NetworkMode.ROUTED.name().equalsIgnoreCase(getNetworkMode())) { supportedServices.add(Gateway.getName()); } - if (getNsxSupportsLbService()) { + if (getNsxSupportsLbService() || isNetrisNatted(getProvider(), getNetworkMode())) { supportedServices.add(Lb.getName()); } } @@ -220,10 +227,8 @@ public Map> getServiceProviders() { Iterator> iter = servicesCollection.iterator(); while (iter.hasNext()) { Map obj = iter.next(); - if (logger.isTraceEnabled()) { - logger.trace("service provider entry specified: " + obj); - } - HashMap services = (HashMap) obj; + logger.trace("Service provider entry specified: {}", obj); + HashMap services = (HashMap)obj; String service = services.get("service"); String provider = services.get("provider"); List providerList = null; @@ -259,7 +264,7 @@ else if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMo serviceProviderMap.put(service, List.of(provider)); } } - if (!getNsxSupportsLbService()) { + if ((isNsxWithoutLb(getProvider(), getNsxSupportsLbService())) || isNetrisRouted(getProvider(), getNetworkMode())) { serviceProviderMap.remove(Lb.getName()); } } @@ -309,6 +314,10 @@ public String getRoutingMode() { return routingMode; } + public boolean isConserveMode() { + return BooleanUtils.toBoolean(conserveMode); + } + @Override public void create() throws ResourceAllocationException { VpcOffering vpcOff = _vpcProvSvc.createVpcOffering(this); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/DeletePrivateGatewayCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/DeletePrivateGatewayCmd.java index d104edc381bc..a6b0538062b1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/DeletePrivateGatewayCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/DeletePrivateGatewayCmd.java @@ -45,7 +45,7 @@ public class DeletePrivateGatewayCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = PrivateGatewayResponse.class, required = true, description = "the ID of the private gateway") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = PrivateGatewayResponse.class, required = true, description = "The ID of the private gateway") private Long id; ///////////////////////////////////////////////////// @@ -66,7 +66,7 @@ public String getEventType() { @Override public String getEventDescription() { - return ("Deleting private gateway id=" + id); + return "Deleting private gateway with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -76,7 +76,7 @@ public long getEntityOwnerId() { @Override public void execute() throws ResourceUnavailableException, ConcurrentOperationException { - CallContext.current().setEventDetails("Network ACL Id: " + id); + CallContext.current().setEventDetails("Network ACL ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _vpcService.deleteVpcPrivateGateway(id); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/DeleteVPCOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/DeleteVPCOfferingCmd.java index 6aa0c3f3afce..f579eeb87e4d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/DeleteVPCOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/DeleteVPCOfferingCmd.java @@ -38,7 +38,7 @@ public class DeleteVPCOfferingCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VpcOfferingResponse.class, required = true, description = "the ID of the VPC offering") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VpcOfferingResponse.class, required = true, description = "The ID of the VPC offering") private Long id; ///////////////////////////////////////////////////// @@ -76,7 +76,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Deleting VPC offering id=" + getId(); + return "Deleting VPC offering with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/UpdateVPCOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/UpdateVPCOfferingCmd.java index b59837281ef3..97f30f6fa2ef 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/UpdateVPCOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/UpdateVPCOfferingCmd.java @@ -16,7 +16,6 @@ // under the License. package org.apache.cloudstack.api.command.admin.vpc; -import java.util.ArrayList; import java.util.List; import org.apache.cloudstack.api.APICommand; @@ -26,49 +25,47 @@ import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.offering.DomainAndZoneIdResolver; import org.apache.cloudstack.api.response.VpcOfferingResponse; -import org.apache.commons.lang3.StringUtils; -import com.cloud.dc.DataCenter; -import com.cloud.domain.Domain; import com.cloud.event.EventTypes; -import com.cloud.exception.InvalidParameterValueException; import com.cloud.network.vpc.VpcOffering; import com.cloud.user.Account; @APICommand(name = "updateVPCOffering", description = "Updates VPC offering", responseObject = VpcOfferingResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class UpdateVPCOfferingCmd extends BaseAsyncCmd { +public class UpdateVPCOfferingCmd extends BaseAsyncCmd implements DomainAndZoneIdResolver { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VpcOfferingResponse.class, required = true, description = "the id of the VPC offering") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VpcOfferingResponse.class, required = true, description = "The ID of the VPC offering") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the VPC offering") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the VPC offering") private String vpcOffName; - @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "the display text of the VPC offering") + @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "The display text of the VPC offering") private String displayText; - @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "update state for the VPC offering; " + "supported states - Enabled/Disabled") + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "Update state for the VPC offering; " + "supported states - Enabled/Disabled") private String state; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.STRING, - description = "the ID of the containing domain(s) as comma separated string, public for public offerings", + description = "The ID of the containing domain(s) as comma separated string, public for public offerings", length = 4096) private String domainIds; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.STRING, - description = "the ID of the containing zone(s) as comma separated string, all for all zones offerings", + description = "The ID of the containing zone(s) as comma separated string, all for all zones offerings", + length = 4096, since = "4.13") private String zoneIds; - @Parameter(name = ApiConstants.SORT_KEY, type = CommandType.INTEGER, description = "sort key of the VPC offering, integer") + @Parameter(name = ApiConstants.SORT_KEY, type = CommandType.INTEGER, description = "Sort key of the VPC offering, integer") private Integer sortKey; ///////////////////////////////////////////////////// @@ -92,63 +89,11 @@ public String getState() { } public List getDomainIds() { - List validDomainIds = new ArrayList<>(); - if (StringUtils.isNotEmpty(domainIds)) { - if (domainIds.contains(",")) { - String[] domains = domainIds.split(","); - for (String domain : domains) { - Domain validDomain = _entityMgr.findByUuid(Domain.class, domain.trim()); - if (validDomain != null) { - validDomainIds.add(validDomain.getId()); - } else { - throw new InvalidParameterValueException("Failed to create VPC offering because invalid domain has been specified."); - } - } - } else { - domainIds = domainIds.trim(); - if (!domainIds.matches("public")) { - Domain validDomain = _entityMgr.findByUuid(Domain.class, domainIds.trim()); - if (validDomain != null) { - validDomainIds.add(validDomain.getId()); - } else { - throw new InvalidParameterValueException("Failed to create VPC offering because invalid domain has been specified."); - } - } - } - } else { - validDomainIds.addAll(_vpcProvSvc.getVpcOfferingDomains(id)); - } - return validDomainIds; + return resolveDomainIds(domainIds, id, _vpcProvSvc::getVpcOfferingDomains, "VPC offering"); } public List getZoneIds() { - List validZoneIds = new ArrayList<>(); - if (StringUtils.isNotEmpty(zoneIds)) { - if (zoneIds.contains(",")) { - String[] zones = zoneIds.split(","); - for (String zone : zones) { - DataCenter validZone = _entityMgr.findByUuid(DataCenter.class, zone.trim()); - if (validZone != null) { - validZoneIds.add(validZone.getId()); - } else { - throw new InvalidParameterValueException("Failed to create VPC offering because invalid zone has been specified."); - } - } - } else { - zoneIds = zoneIds.trim(); - if (!zoneIds.matches("all")) { - DataCenter validZone = _entityMgr.findByUuid(DataCenter.class, zoneIds.trim()); - if (validZone != null) { - validZoneIds.add(validZone.getId()); - } else { - throw new InvalidParameterValueException("Failed to create VPC offering because invalid zone has been specified."); - } - } - } - } else { - validZoneIds.addAll(_vpcProvSvc.getVpcOfferingZones(id)); - } - return validZoneIds; + return resolveZoneIds(zoneIds, id, _vpcProvSvc::getVpcOfferingZones, "VPC offering"); } public Integer getSortKey() { @@ -182,7 +127,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Updating VPC offering id=" + getId(); + return "Updating VPC offering with ID:" + getResourceUuid(ApiConstants.ID); } @Override 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 f3c1bab260b9..d7f448d6ece6 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 @@ -42,28 +42,28 @@ public class CreateZoneCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.DNS1, type = CommandType.STRING, required = true, description = "the first DNS for the Zone") + @Parameter(name = ApiConstants.DNS1, type = CommandType.STRING, required = true, description = "The first DNS for the Zone") private String dns1; - @Parameter(name = ApiConstants.DNS2, type = CommandType.STRING, description = "the second DNS for the Zone") + @Parameter(name = ApiConstants.DNS2, type = CommandType.STRING, description = "The second DNS for the Zone") private String dns2; - @Parameter(name = ApiConstants.IP6_DNS1, type = CommandType.STRING, description = "the first DNS for IPv6 network in the Zone") + @Parameter(name = ApiConstants.IP6_DNS1, type = CommandType.STRING, description = "The first DNS for IPv6 network in the Zone") private String ip6Dns1; - @Parameter(name = ApiConstants.IP6_DNS2, type = CommandType.STRING, description = "the second DNS for IPv6 network in the Zone") + @Parameter(name = ApiConstants.IP6_DNS2, type = CommandType.STRING, description = "The second DNS for IPv6 network in the Zone") private String ip6Dns2; - @Parameter(name = ApiConstants.GUEST_CIDR_ADDRESS, type = CommandType.STRING, description = "the guest CIDR address for the Zone") + @Parameter(name = ApiConstants.GUEST_CIDR_ADDRESS, type = CommandType.STRING, description = "The guest CIDR address for the Zone") private String guestCidrAddress; - @Parameter(name = ApiConstants.INTERNAL_DNS1, type = CommandType.STRING, required = true, description = "the first internal DNS for the Zone") + @Parameter(name = ApiConstants.INTERNAL_DNS1, type = CommandType.STRING, required = true, description = "The first internal DNS for the Zone") private String internalDns1; - @Parameter(name = ApiConstants.INTERNAL_DNS2, type = CommandType.STRING, description = "the second internal DNS for the Zone") + @Parameter(name = ApiConstants.INTERNAL_DNS2, type = CommandType.STRING, description = "The second internal DNS for the Zone") private String internalDns2; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the Zone") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "The name of the Zone") private String zoneName; @Parameter(name = ApiConstants.DOMAIN, type = CommandType.STRING, description = "Network domain name for the networks in the zone") @@ -72,22 +72,22 @@ public class CreateZoneCmd extends BaseCmd { @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "the ID of the containing domain, null for public zones") + description = "The ID of the containing domain, null for public zones") private Long domainId; - @Parameter(name = ApiConstants.NETWORK_TYPE, type = CommandType.STRING, required = true, description = "network type of the zone, can be Basic or Advanced") + @Parameter(name = ApiConstants.NETWORK_TYPE, type = CommandType.STRING, required = true, description = "Network type of the zone, can be Basic or Advanced") private String networkType; @Parameter(name = ApiConstants.ALLOCATION_STATE, type = CommandType.STRING, description = "Allocation state of this Zone for allocation of new resources") private String allocationState; - @Parameter(name = ApiConstants.SECURITY_GROUP_EANBLED, type = CommandType.BOOLEAN, description = "true if network is security group enabled, false otherwise") + @Parameter(name = ApiConstants.SECURITY_GROUP_EANBLED, type = CommandType.BOOLEAN, description = "True if network is security group enabled, false otherwise") private Boolean securitygroupenabled; - @Parameter(name = ApiConstants.LOCAL_STORAGE_ENABLED, type = CommandType.BOOLEAN, description = "true if local storage offering enabled, false otherwise") + @Parameter(name = ApiConstants.LOCAL_STORAGE_ENABLED, type = CommandType.BOOLEAN, description = "True if local storage offering enabled, false otherwise") private Boolean localStorageEnabled; - @Parameter(name = ApiConstants.IS_EDGE, type = CommandType.BOOLEAN, description = "true if the zone is an edge zone, false otherwise", since = "4.18.0") + @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, diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/DeleteZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/DeleteZoneCmd.java index b89636c6fe52..28d14a318753 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/DeleteZoneCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/DeleteZoneCmd.java @@ -38,7 +38,7 @@ public class DeleteZoneCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "the ID of the Zone") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "The ID of the Zone") private Long id; ///////////////////////////////////////////////////// @@ -60,7 +60,7 @@ public long getEntityOwnerId() { @Override public void execute() { - CallContext.current().setEventDetails("Zone Id: " + getId()); + CallContext.current().setEventDetails("Zone ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _configService.deleteZone(this); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/MarkDefaultZoneForAccountCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/MarkDefaultZoneForAccountCmd.java index 5d3f5dcd47fa..42040290a411 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/MarkDefaultZoneForAccountCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/MarkDefaultZoneForAccountCmd.java @@ -95,7 +95,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Marking account with the default zone: " + getDefaultZoneId(); + return "Marking zone with ID: " + getResourceUuid(ApiConstants.ZONE_ID) + " as default for account " + getResourceUuid(ApiConstants.ACCOUNT) + " in domain: " + getResourceUuid(ApiConstants.DOMAIN_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/UpdateZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/UpdateZoneCmd.java index 1b2793d3e158..888ee6603ddf 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/UpdateZoneCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/UpdateZoneCmd.java @@ -42,43 +42,43 @@ public class UpdateZoneCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.DNS1, type = CommandType.STRING, description = "the first DNS for the Zone") + @Parameter(name = ApiConstants.DNS1, type = CommandType.STRING, description = "The first DNS for the Zone") private String dns1; - @Parameter(name = ApiConstants.DNS2, type = CommandType.STRING, description = "the second DNS for the Zone") + @Parameter(name = ApiConstants.DNS2, type = CommandType.STRING, description = "The second DNS for the Zone") private String dns2; - @Parameter(name = ApiConstants.IP6_DNS1, type = CommandType.STRING, description = "the first DNS for IPv6 network in the Zone") + @Parameter(name = ApiConstants.IP6_DNS1, type = CommandType.STRING, description = "The first DNS for IPv6 network in the Zone") private String ip6Dns1; - @Parameter(name = ApiConstants.IP6_DNS2, type = CommandType.STRING, description = "the second DNS for IPv6 network in the Zone") + @Parameter(name = ApiConstants.IP6_DNS2, type = CommandType.STRING, description = "The second DNS for IPv6 network in the Zone") private String ip6Dns2; - @Parameter(name = ApiConstants.GUEST_CIDR_ADDRESS, type = CommandType.STRING, description = "the guest CIDR address for the Zone") + @Parameter(name = ApiConstants.GUEST_CIDR_ADDRESS, type = CommandType.STRING, description = "The guest CIDR address for the Zone") private String guestCidrAddress; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "the ID of the Zone") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "The ID of the Zone") private Long id; - @Parameter(name = ApiConstants.INTERNAL_DNS1, type = CommandType.STRING, description = "the first internal DNS for the Zone") + @Parameter(name = ApiConstants.INTERNAL_DNS1, type = CommandType.STRING, description = "The first internal DNS for the Zone") private String internalDns1; - @Parameter(name = ApiConstants.INTERNAL_DNS2, type = CommandType.STRING, description = "the second internal DNS for the Zone") + @Parameter(name = ApiConstants.INTERNAL_DNS2, type = CommandType.STRING, description = "The second internal DNS for the Zone") private String internalDns2; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the Zone") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the Zone") private String zoneName; - @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "updates a private zone to public if set, but not vice-versa") + @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "Updates a private zone to public if set, but not vice-versa") private Boolean isPublic; @Parameter(name = ApiConstants.ALLOCATION_STATE, type = CommandType.STRING, description = "Allocation state of this cluster for allocation of new resources") private String allocationState; - @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "the details for the Zone") + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "The details for the Zone") private Map details; - @Parameter(name = ApiConstants.DHCP_PROVIDER, type = CommandType.STRING, description = "the dhcp Provider for the Zone") + @Parameter(name = ApiConstants.DHCP_PROVIDER, type = CommandType.STRING, description = "The DHCP Provider for the Zone") private String dhcpProvider; @Parameter(name = ApiConstants.DOMAIN, @@ -86,13 +86,13 @@ public class UpdateZoneCmd extends BaseCmd { description = "Network domain name for the networks in the zone; empty string will update domain with NULL value") private String domain; - @Parameter(name = ApiConstants.DNS_SEARCH_ORDER, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the dns search order list") + @Parameter(name = ApiConstants.DNS_SEARCH_ORDER, type = CommandType.LIST, collectionType = CommandType.STRING, description = "The DNS search order list") private List dnsSearchOrder; - @Parameter(name = ApiConstants.LOCAL_STORAGE_ENABLED, type = CommandType.BOOLEAN, description = "true if local storage offering enabled, false otherwise") + @Parameter(name = ApiConstants.LOCAL_STORAGE_ENABLED, type = CommandType.BOOLEAN, description = "True if local storage offering enabled, false otherwise") private Boolean localStorageEnabled; - @Parameter(name = ApiConstants.SORT_KEY, type = CommandType.INTEGER, description = "sort key of the zone, integer") + @Parameter(name = ApiConstants.SORT_KEY, type = CommandType.INTEGER, description = "Sort key of the zone, integer") private Integer sortKey; ///////////////////////////////////////////////////// @@ -178,7 +178,7 @@ public long getEntityOwnerId() { @Override public void execute() { - CallContext.current().setEventDetails("Zone Id: " + getId()); + CallContext.current().setEventDetails("Zone ID: " + getResourceUuid(ApiConstants.ID)); DataCenter result = _configService.editZone(this); if (result != null) { ZoneResponse response = _responseGenerator.createZoneResponse(ResponseView.Full, result, false, false); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/offering/DomainAndZoneIdResolver.java b/api/src/main/java/org/apache/cloudstack/api/command/offering/DomainAndZoneIdResolver.java new file mode 100644 index 000000000000..b302c4a9beec --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/offering/DomainAndZoneIdResolver.java @@ -0,0 +1,114 @@ +// 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.offering; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.LongFunction; + +import com.cloud.dc.DataCenter; +import com.cloud.domain.Domain; +import com.cloud.exception.InvalidParameterValueException; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * Helper for commands that accept a domainIds or zoneIds string and need to + * resolve them to lists of IDs, falling back to an offering-specific + * default provider. + */ +public interface DomainAndZoneIdResolver { + /** + * Parse the provided domainIds string and return a list of domain IDs. + * If domainIds is empty, the defaultDomainsProvider will be invoked with the + * provided resource id to obtain the current domains. + */ + default List resolveDomainIds(final String domainIds, final Long id, final LongFunction> defaultDomainsProvider, final String resourceTypeName) { + final List validDomainIds = new ArrayList<>(); + final BaseCmd base = (BaseCmd) this; + final Logger logger = LogManager.getLogger(base.getClass()); + + if (StringUtils.isEmpty(domainIds)) { + if (defaultDomainsProvider != null) { + final List defaults = defaultDomainsProvider.apply(id); + if (defaults != null) { + validDomainIds.addAll(defaults); + } + } + return validDomainIds; + } + + final String[] domains = domainIds.split(","); + final String type = (resourceTypeName == null || resourceTypeName.isEmpty()) ? "offering" : resourceTypeName; + for (String domain : domains) { + final String trimmed = domain == null ? "" : domain.trim(); + if (trimmed.isEmpty() || "public".equalsIgnoreCase(trimmed)) { + continue; + } + + final Domain validDomain = base._entityMgr.findByUuid(Domain.class, trimmed); + if (validDomain == null) { + logger.warn("Invalid domain specified for {}", type); + throw new InvalidParameterValueException("Failed to create " + type + " because invalid domain has been specified."); + } + validDomainIds.add(validDomain.getId()); + } + + return validDomainIds; + } + + /** + * Parse the provided zoneIds string and return a list of zone IDs. + * If zoneIds is empty, the defaultZonesProvider will be invoked with the + * provided resource id to obtain the current zones. + */ + default List resolveZoneIds(final String zoneIds, final Long id, final LongFunction> defaultZonesProvider, final String resourceTypeName) { + final List validZoneIds = new ArrayList<>(); + final BaseCmd base = (BaseCmd) this; + final Logger logger = LogManager.getLogger(base.getClass()); + + if (StringUtils.isEmpty(zoneIds)) { + if (defaultZonesProvider != null) { + final List defaults = defaultZonesProvider.apply(id); + if (defaults != null) { + validZoneIds.addAll(defaults); + } + } + return validZoneIds; + } + + final String[] zones = zoneIds.split(","); + final String type = (resourceTypeName == null || resourceTypeName.isEmpty()) ? "offering" : resourceTypeName; + for (String zone : zones) { + final String trimmed = zone == null ? "" : zone.trim(); + if (trimmed.isEmpty() || "all".equalsIgnoreCase(trimmed)) { + continue; + } + + final DataCenter validZone = base._entityMgr.findByUuid(DataCenter.class, trimmed); + if (validZone == null) { + logger.warn("Invalid zone specified for {}: {}", type, trimmed); + throw new InvalidParameterValueException("Failed to create " + type + " because invalid zone has been specified."); + } + validZoneIds.add(validZone.getId()); + } + + return validZoneIds; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddAccountToProjectCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddAccountToProjectCmd.java index 2fbcb6df1ccb..63a0a6ca51e9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddAccountToProjectCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddAccountToProjectCmd.java @@ -18,6 +18,7 @@ import java.util.List; +import com.cloud.exception.ResourceAllocationException; import org.apache.cloudstack.api.ApiArgValidator; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.BaseCmd; @@ -40,7 +41,7 @@ import com.cloud.projects.Project; import com.cloud.projects.ProjectAccount; -@APICommand(name = "addAccountToProject", description = "Adds account to a project", responseObject = SuccessResponse.class, since = "3.0.0", +@APICommand(name = "addAccountToProject", description = "Adds Account to a project", responseObject = SuccessResponse.class, since = "3.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class AddAccountToProjectCmd extends BaseAsyncCmd { @@ -53,13 +54,13 @@ public class AddAccountToProjectCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = ProjectResponse.class, required = true, - description = "ID of the project to add the account to") + description = "ID of the project to add the Account to") private Long projectId; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "name of the account to be added to the project") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "Name of the Account to be added to the project") private String accountName; - @Parameter(name = ApiConstants.EMAIL, type = CommandType.STRING, description = "email to which invitation to the project is going to be sent") + @Parameter(name = ApiConstants.EMAIL, type = CommandType.STRING, description = "Email to which invitation to the project is going to be sent") private String email; @Parameter(name = ApiConstants.PROJECT_ROLE_ID, type = CommandType.UUID, entityType = ProjectRoleResponse.class, @@ -67,7 +68,7 @@ public class AddAccountToProjectCmd extends BaseAsyncCmd { private Long projectRoleId; @Parameter(name = ApiConstants.ROLE_TYPE, type = BaseCmd.CommandType.STRING, - description = "Project role type to be assigned to the user - Admin/Regular; default: Regular") + description = "Project role type to be assigned to the User - Admin/Regular; default: Regular") private String roleType; ///////////////////////////////////////////////////// @@ -106,18 +107,18 @@ public ProjectAccount.Role getRoleType() { ///////////////////////////////////////////////////// @Override - public void execute() { + public void execute() throws ResourceAllocationException { if (accountName == null && email == null) { throw new InvalidParameterValueException("Either accountName or email is required"); } - CallContext.current().setEventDetails("Project ID: " + projectId + "; accountName " + accountName); + CallContext.current().setEventDetails("Project ID: " + getResourceUuid(ApiConstants.PROJECT_ID) + "; accountName " + accountName); boolean result = _projectService.addAccountToProject(getProjectId(), getAccountName(), getEmail(), getProjectRoleId(), getRoleType()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add account to the project"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add Account to the project"); } } @@ -146,10 +147,12 @@ public String getEventType() { @Override public String getEventDescription() { + String projectUuid = getResourceUuid(ApiConstants.PROJECT_ID); + if (accountName != null) { - return "Adding account " + getAccountName() + " to project: " + getProjectId(); + return "Adding account " + getAccountName() + " to project: " + projectUuid; } else { - return "Sending invitation to email " + email + " to join project: " + getProjectId(); + return "Sending invitation to email " + email + " to join project: " + projectUuid; } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddUserToProjectCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddUserToProjectCmd.java index 9cd845c774c5..683522039b17 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddUserToProjectCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddUserToProjectCmd.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.user.account; +import com.cloud.exception.ResourceAllocationException; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiArgValidator; @@ -38,7 +39,7 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.projects.ProjectAccount; -@APICommand(name = "addUserToProject", description = "Adds user to a project", responseObject = SuccessResponse.class, since = "4.14", +@APICommand(name = "addUserToProject", description = "Adds User to a project", responseObject = SuccessResponse.class, since = "4.14", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin, RoleType.DomainAdmin, RoleType.ResourceAdmin, RoleType.User}) public class AddUserToProjectCmd extends BaseAsyncCmd { @@ -50,13 +51,13 @@ public class AddUserToProjectCmd extends BaseAsyncCmd { type = BaseCmd.CommandType.UUID, entityType = ProjectResponse.class, required = true, - description = "ID of the project to add the user to") + description = "ID of the project to add the User to") private Long projectId; - @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, required = true, description = "Name of the user to be added to the project") + @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, required = true, description = "Name of the User to be added to the project") private String username; - @Parameter(name = ApiConstants.EMAIL, type = CommandType.STRING, description = "email ID of user to which invitation to the project is going to be sent") + @Parameter(name = ApiConstants.EMAIL, type = CommandType.STRING, description = "Email ID of User to which invitation to the project is going to be sent") private String email; @Parameter(name = ApiConstants.PROJECT_ROLE_ID, type = BaseCmd.CommandType.UUID, entityType = ProjectRoleResponse.class, @@ -64,7 +65,7 @@ public class AddUserToProjectCmd extends BaseAsyncCmd { private Long projectRoleId; @Parameter(name = ApiConstants.ROLE_TYPE, type = BaseCmd.CommandType.STRING, - description = "Project role type to be assigned to the user - Admin/Regular") + description = "Project role type to be assigned to the User - Admin/Regular") private String roleType; ///////////////////////////////////////////////////// @@ -103,7 +104,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Adding user " + getUsername() + " to project: " + getProjectId(); + return "Adding User " + getUsername() + " to Project: " + getResourceUuid(ApiConstants.PROJECT_ID); } ///////////////////////////////////////////////////// @@ -111,14 +112,14 @@ public String getEventDescription() { ///////////////////////////////////////////////////// @Override - public void execute() { + public void execute() throws ResourceAllocationException { validateInput(); boolean result = _projectService.addUserToProject(getProjectId(), getUsername(), getEmail(), getProjectRoleId(), getRoleType()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add account to the project"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add Account to the project"); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/account/DeleteAccountFromProjectCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/account/DeleteAccountFromProjectCmd.java index 5e0977938a30..74e6f2c804c7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/account/DeleteAccountFromProjectCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/account/DeleteAccountFromProjectCmd.java @@ -34,7 +34,7 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.projects.Project; -@APICommand(name = "deleteAccountFromProject", description = "Deletes account from the project", responseObject = SuccessResponse.class, since = "3.0.0", +@APICommand(name = "deleteAccountFromProject", description = "Deletes Account from the project", responseObject = SuccessResponse.class, since = "3.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DeleteAccountFromProjectCmd extends BaseAsyncCmd { @@ -46,10 +46,10 @@ public class DeleteAccountFromProjectCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = ProjectResponse.class, required = true, - description = "ID of the project to remove the account from") + description = "ID of the project to remove the Account from") private Long projectId; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, required = true, description = "name of the account to be removed from the project") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, required = true, description = "Name of the Account to be removed from the project") private String accountName; ///////////////////////////////////////////////////// @@ -70,13 +70,13 @@ public String getAccountName() { @Override public void execute() { - CallContext.current().setEventDetails("Project ID: " + projectId + "; accountName " + accountName); + CallContext.current().setEventDetails("Project ID: " + getResourceUuid(ApiConstants.PROJECT_ID) + "; accountName " + accountName); boolean result = _projectService.deleteAccountFromProject(projectId, accountName); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete account from the project"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete Account from the project"); } } @@ -103,7 +103,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Removing account " + accountName + " from project: " + projectId; + return "Removing Account " + accountName + " from project: " + getResourceUuid(ApiConstants.PROJECT_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/account/DeleteUserFromProjectCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/account/DeleteUserFromProjectCmd.java index 0731d8378047..2677b206bdc1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/account/DeleteUserFromProjectCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/account/DeleteUserFromProjectCmd.java @@ -37,7 +37,7 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.projects.Project; -@APICommand(name = "deleteUserFromProject", description = "Deletes user from the project", responseObject = SuccessResponse.class, since = "4.15.0", +@APICommand(name = "deleteUserFromProject", description = "Deletes User from the project", responseObject = SuccessResponse.class, since = "4.15.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin, RoleType.DomainAdmin, RoleType.ResourceAdmin, RoleType.User}) public class DeleteUserFromProjectCmd extends BaseAsyncCmd { @@ -48,11 +48,11 @@ public class DeleteUserFromProjectCmd extends BaseAsyncCmd { type = BaseCmd.CommandType.UUID, entityType = ProjectResponse.class, required = true, - description = "ID of the project to remove the user from") + description = "ID of the project to remove the User from") private Long projectId; @Parameter(name = ApiConstants.USER_ID, type = BaseCmd.CommandType.UUID, entityType = UserResponse.class, - required = true, description = "Id of the user to be removed from the project") + required = true, description = "Id of the User to be removed from the project") private Long userId; ///////////////////////////////////////////////////// @@ -78,7 +78,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Removing user " + userId + " from project: " + projectId; + return "Removing User " + getResourceUuid(ApiConstants.USER_ID) + " from project: " + getResourceUuid(ApiConstants.PROJECT_ID); } @Override @@ -107,13 +107,13 @@ public ApiCommandResourceType getApiResourceType() { @Override public void execute() { - CallContext.current().setEventDetails("Project ID: " + projectId + "; user ID: " + userId); + CallContext.current().setEventDetails("Project ID: " + getResourceUuid(ApiConstants.PROJECT_ID) + "; User ID: " + getResourceUuid(ApiConstants.USER_ID)); boolean result = _projectService.deleteUserFromProject(getProjectId(), getUserId()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete account from the project"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete Account from the project"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/account/ListAccountsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/account/ListAccountsCmd.java index bd9ab30f4f1f..dd0ece878902 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/account/ListAccountsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/account/ListAccountsCmd.java @@ -39,7 +39,7 @@ import com.cloud.server.ResourceTag; import com.cloud.user.Account; -@APICommand(name = "listAccounts", description = "Lists accounts and provides detailed account information for listed accounts", responseObject = AccountResponse.class, responseView = ResponseView.Restricted, entityType = {Account.class}, +@APICommand(name = "listAccounts", description = "Lists Accounts and provides detailed Account information for listed Accounts", responseObject = AccountResponse.class, responseView = ResponseView.Restricted, entityType = {Account.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class ListAccountsCmd extends BaseListDomainResourcesCmd implements UserCmd { private static final String s_name = "listaccountsresponse"; @@ -50,32 +50,32 @@ public class ListAccountsCmd extends BaseListDomainResourcesCmd implements UserC @Parameter(name = ApiConstants.ACCOUNT_TYPE, type = CommandType.INTEGER, - description = "list accounts by account type. Valid account types are 1 (admin), 2 (domain-admin), and 0 (user).") + description = "List Accounts by Account type. Valid Account types are 1 (admin), 2 (domain-admin), and 0 (user).") private Integer accountType; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "list account by account ID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "List Account by Account ID") private Long id; - @Parameter(name = ApiConstants.IS_CLEANUP_REQUIRED, type = CommandType.BOOLEAN, description = "list accounts by cleanuprequired attribute (values are true or false)") + @Parameter(name = ApiConstants.IS_CLEANUP_REQUIRED, type = CommandType.BOOLEAN, description = "List accounts by cleanuprequired attribute (values are true or false)") private Boolean cleanupRequired; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "list account by account name") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "List account by account name") private String searchName; - @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "list accounts by state. Valid states are enabled, disabled, and locked.") + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "List accounts by state. Valid states are enabled, disabled, and locked.") private String state; @Parameter(name = ApiConstants.DETAILS, type = CommandType.LIST, collectionType = CommandType.STRING, - description = "comma separated list of account details requested, value can be a list of [ all, resource, min]") + description = "Comma separated list of account details requested, value can be a list of [ all, resource, min]") private List viewDetails; @Parameter(name = ApiConstants.API_KEY_ACCESS, type = CommandType.STRING, description = "List accounts by the Api key access value", since = "4.20.1.0", authorized = {RoleType.Admin}) private String apiKeyAccess; @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, - description = "flag to display the resource icon for accounts") + description = "Flag to display the resource icon for accounts") private Boolean showIcon; @Parameter(name = ApiConstants.TAG, type = CommandType.STRING, description = "Tag for resource type to return usage", since = "4.20.0") diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/account/ListProjectAccountsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/account/ListProjectAccountsCmd.java index 21aedc7d7725..aeacf7c60d56 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/account/ListProjectAccountsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/account/ListProjectAccountsCmd.java @@ -29,7 +29,7 @@ import com.cloud.user.Account; -@APICommand(name = "listProjectAccounts", description = "Lists project's accounts", responseObject = ProjectResponse.class, since = "3.0.0", +@APICommand(name = "listProjectAccounts", description = "Lists project's Accounts", responseObject = ProjectResponse.class, since = "3.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListProjectAccountsCmd extends BaseListCmd { @@ -41,16 +41,16 @@ public class ListProjectAccountsCmd extends BaseListCmd { @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, required = true, description = "ID of the project") private Long projectId; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "list accounts of the project by account name") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "List Accounts of the project by Account name") private String accountName; - @Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class, description = "list invitation by user ID") + @Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class, description = "List invitation by User ID") private Long userId; - @Parameter(name = ApiConstants.ROLE, type = CommandType.STRING, description = "list accounts of the project by role") + @Parameter(name = ApiConstants.ROLE, type = CommandType.STRING, description = "List Accounts of the project by role") private String role; - @Parameter(name = ApiConstants.PROJECT_ROLE_ID, type = CommandType.UUID, entityType = ProjectRoleResponse.class, description = "list accounts of the project by project role id") + @Parameter(name = ApiConstants.PROJECT_ROLE_ID, type = CommandType.UUID, entityType = ProjectRoleResponse.class, description = "List Accounts of the project by project role id") private Long projectRoleId; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/address/AssociateIPAddrCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/address/AssociateIPAddrCmd.java index 5ea144785167..a62f9f316606 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/address/AssociateIPAddrCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/address/AssociateIPAddrCmd.java @@ -58,7 +58,7 @@ import com.cloud.user.Account; @APICommand(name = "associateIpAddress", - description = "Acquires and associates a public IP to an account. Either of the parameters are required, i.e. either zoneId, or networkId, or vpcId ", + description = "Acquires and associates a public IP to an Account. Either of the parameters are required, i.e. either zoneId, or networkId, or vpcId ", responseObject = IPAddressResponse.class, responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, @@ -72,58 +72,58 @@ public class AssociateIPAddrCmd extends BaseAsyncCreateCmd implements UserCmd { @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, - description = "the account to associate with this IP address") + description = "The Account to associate with this IP address") private String accountName; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "the ID of the domain to associate with this IP address") + description = "The ID of the domain to associate with this IP address") private Long domainId; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, - description = "the ID of the availability zone you want to acquire an public IP address from") + description = "The ID of the availability zone you want to acquire an public IP address from") private Long zoneId; @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, - description = "The network this IP address should be associated to.") + description = "The Network this IP address should be associated to.") private Long networkId; @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, - description = "Deploy VM for the project") + description = "Deploy Instance for the project") private Long projectId; @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, - description = "the VPC you want the IP address to be associated with") + description = "The VPC you want the IP address to be associated with") private Long vpcId; @Parameter(name = ApiConstants.IS_PORTABLE, type = BaseCmd.CommandType.BOOLEAN, - description = "should be set to true if public IP is required to be transferable across zones, if not specified defaults to false") + description = "Should be set to true if public IP is required to be transferable across zones, if not specified defaults to false") private Boolean isPortable; @Parameter(name = ApiConstants.REGION_ID, type = CommandType.INTEGER, entityType = RegionResponse.class, required = false, - description = "region ID from where portable IP is to be associated.") + description = "Region ID from where portable IP is to be associated.") private Integer regionId; @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, - description = "an optional field, whether to the display the IP to the end user or not", since = "4.4", + description = "An optional field, whether to the display the IP to the end User or not", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; - @Parameter(name=ApiConstants.IP_ADDRESS, type=CommandType.STRING, description="IP Address to be associated") + @Parameter(name=ApiConstants.IP_ADDRESS, type=CommandType.STRING, description = "IP Address to be associated") private String ipAddress; ///////////////////////////////////////////////////// @@ -198,21 +198,21 @@ public Long getNetworkId() { List networks = _networkService.getIsolatedNetworksOwnedByAccountInZone(getZoneId(), _accountService.getAccount(getEntityOwnerId())); if (networks.size() == 0) { String domain = _domainService.getDomain(getDomainId()).getName(); - throw new InvalidParameterValueException("Account name=" + getAccountName() + " domain=" + domain + " doesn't have virtual networks in zone=" + + throw new InvalidParameterValueException("Account name=" + getAccountName() + " domain=" + domain + " doesn't have virtual Networks in zone=" + zone.getName()); } if (networks.size() < 1) { - throw new InvalidParameterValueException("Account doesn't have any isolated networks in the zone"); + throw new InvalidParameterValueException("Account doesn't have any isolated Networks in the zone"); } else if (networks.size() > 1) { - throw new InvalidParameterValueException("Account has more than one isolated network in the zone"); + throw new InvalidParameterValueException("Account has more than one isolated Network in the zone"); } return networks.get(0).getId(); } else { Network defaultGuestNetwork = _networkService.getExclusiveGuestNetwork(zoneId); if (defaultGuestNetwork == null) { - throw new InvalidParameterValueException("Unable to find a default guest network for account " + getAccountName() + " in domain ID=" + getDomainId()); + throw new InvalidParameterValueException("Unable to find a default guest Network for Account " + getAccountName() + " in domain ID=" + getDomainId()); } else { return defaultGuestNetwork.getId(); } @@ -253,7 +253,7 @@ public long getEntityOwnerId() { } else if (networkId != null) { Network network = _networkService.getNetwork(networkId); if (network == null) { - throw new InvalidParameterValueException("Unable to find network by network id specified"); + throw new InvalidParameterValueException("Unable to find Network by network id specified"); } NetworkOffering offering = _entityMgr.findById(NetworkOffering.class, network.getNetworkOfferingId()); @@ -334,7 +334,7 @@ public void create() throws ResourceAllocationException { @Override public void execute() throws ResourceUnavailableException, ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException { - CallContext.current().setEventDetails("IP ID: " + getEntityId()); + CallContext.current().setEventDetails("IP address ID: " + getEntityUuid()); IpAddress result = null; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/address/DisassociateIPAddrCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/address/DisassociateIPAddrCmd.java index f4c06e512f07..835a2a69e3c9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/address/DisassociateIPAddrCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/address/DisassociateIPAddrCmd.java @@ -35,7 +35,7 @@ import com.cloud.network.IpAddress; import com.cloud.user.Account; -@APICommand(name = "disassociateIpAddress", description = "Disassociates an IP address from the account.", responseObject = SuccessResponse.class, +@APICommand(name = "disassociateIpAddress", description = "Disassociates an IP address from the Account.", responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, entityType = { IpAddress.class }) public class DisassociateIPAddrCmd extends BaseAsyncCmd { @@ -44,7 +44,7 @@ public class DisassociateIPAddrCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = IPAddressResponse.class, description = "the ID of the public IP address" + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = IPAddressResponse.class, description = "The ID of the public IP address" + " to disassociate. Mutually exclusive with the ipaddress parameter") private Long id; @@ -82,7 +82,7 @@ public Long getIpAddressId() { @Override public void execute() throws InsufficientAddressCapacityException { Long ipAddressId = getIpAddressId(); - CallContext.current().setEventDetails("IP ID: " + ipAddressId); + CallContext.current().setEventDetails("IP address ID: " + getResourceUuid(ApiConstants.ID)); boolean result = false; if (!isPortable()) { result = _networkService.releaseIpAddress(ipAddressId); @@ -108,7 +108,7 @@ public String getEventType() { @Override public String getEventDescription() { - return ("Disassociating IP address with ID=" + id); + return ("Disassociating IP address with ID:" + getResourceUuid(ApiConstants.ID)); } @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 357f0c83ed74..d79381e57c4e 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 @@ -50,59 +50,59 @@ public class ListPublicIpAddressesCmd extends BaseListRetrieveOnlyResourceCountC //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ALLOCATED_ONLY, type = CommandType.BOOLEAN, description = "limits search results to allocated public IP addresses") + @Parameter(name = ApiConstants.ALLOCATED_ONLY, type = CommandType.BOOLEAN, description = "Limits search results to allocated public IP addresses") private Boolean allocatedOnly; - @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "lists all public IP addresses by state") + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "Lists all public IP addresses by state. A comma-separated list of states can be passed.") private String state; - @Parameter(name = ApiConstants.FOR_VIRTUAL_NETWORK, type = CommandType.BOOLEAN, description = "the virtual network for the IP address") + @Parameter(name = ApiConstants.FOR_VIRTUAL_NETWORK, type = CommandType.BOOLEAN, description = "The virtual Network for the IP address") private Boolean forVirtualNetwork; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = IPAddressResponse.class, description = "lists IP address by ID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = IPAddressResponse.class, description = "Lists IP address by ID") private Long id; - @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, description = "lists the specified IP address") + @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, description = "Lists the specified IP address") private String ipAddress; - @Parameter(name = ApiConstants.VLAN_ID, type = CommandType.UUID, entityType = VlanIpRangeResponse.class, description = "lists all public IP addresses by VLAN ID") + @Parameter(name = ApiConstants.VLAN_ID, type = CommandType.UUID, entityType = VlanIpRangeResponse.class, description = "Lists all public IP addresses by VLAN ID") private Long vlanId; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "lists all public IP addresses by zone ID") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "Lists all public IP addresses by zone ID") private Long zoneId; - @Parameter(name = ApiConstants.FOR_LOAD_BALANCING, type = CommandType.BOOLEAN, description = "list only IPs used for load balancing") + @Parameter(name = ApiConstants.FOR_LOAD_BALANCING, type = CommandType.BOOLEAN, description = "List only IPs used for load balancing") private Boolean forLoadBalancing; @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, - description = "lists all public IP addresses by physical network ID") + description = "Lists all public IP addresses by physical Network ID") private Long physicalNetworkId; @Parameter(name = ApiConstants.ASSOCIATED_NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, - description = "lists all public IP addresses associated to the network specified") + description = "Lists all public IP addresses associated to the Network specified") private Long associatedNetworkId; @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, - description = "lists all public IP addresses by source network ID", + description = "Lists all public IP addresses by source Network ID", since = "4.13.0") private Long networkId; - @Parameter(name = ApiConstants.IS_SOURCE_NAT, type = CommandType.BOOLEAN, description = "list only source NAT IP addresses") + @Parameter(name = ApiConstants.IS_SOURCE_NAT, type = CommandType.BOOLEAN, description = "List only source NAT IP addresses") private Boolean isSourceNat; - @Parameter(name = ApiConstants.IS_STATIC_NAT, type = CommandType.BOOLEAN, description = "list only static NAT IP addresses") + @Parameter(name = ApiConstants.IS_STATIC_NAT, type = CommandType.BOOLEAN, description = "List only static NAT IP addresses") private Boolean isStaticNat; @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, description = "List IPs belonging to the VPC") private Long vpcId; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; @Parameter(name = ApiConstants.FOR_SYSTEM_VMS, type = CommandType.BOOLEAN, description = "true if range is dedicated for system VMs", since = "4.20.0") diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/address/ReleaseIPAddrCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/address/ReleaseIPAddrCmd.java index effe45c51ed0..2fe94b29346d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/address/ReleaseIPAddrCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/address/ReleaseIPAddrCmd.java @@ -33,7 +33,7 @@ import com.cloud.network.IpAddress; @APICommand(name = "releaseIpAddress", - description = "Releases an IP address from the account.", + description = "Releases an IP address from the Account.", since = "4.17", responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, @@ -46,7 +46,7 @@ public class ReleaseIPAddrCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = IPAddressResponse.class, required = true, description = "the ID of the public IP address" + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = IPAddressResponse.class, required = true, description = "The ID of the public IP address" + " to release") private Long id; @@ -73,7 +73,7 @@ public long getEntityOwnerId() { @Override public void execute() throws InsufficientAddressCapacityException { - CallContext.current().setEventDetails("IP ID: " + getIpAddressId()); + CallContext.current().setEventDetails("IP address ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _networkService.releaseReservedIpAddress(getIpAddressId()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/address/ReserveIPAddrCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/address/ReserveIPAddrCmd.java index e323d413b9ac..7d102abc07fb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/address/ReserveIPAddrCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/address/ReserveIPAddrCmd.java @@ -42,7 +42,7 @@ import com.cloud.user.Account; @APICommand(name = "reserveIpAddress", - description = "Reserve a public IP to an account.", + description = "Reserve a public IP to an Account.", since = "4.17", responseObject = IPAddressResponse.class, responseView = ResponseView.Restricted, @@ -57,24 +57,24 @@ public class ReserveIPAddrCmd extends BaseCmd implements UserCmd { @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, - description = "the account to reserve with this IP address") + description = "The Account to reserve with this IP address") private String accountName; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "the ID of the domain to reserve with this IP address") + description = "The ID of the domain to reserve with this IP address") private Long domainId; @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, - description = "the ID of the project to reserve with this IP address") + description = "The ID of the project to reserve with this IP address") private Long projectId; @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, - description = "an optional field, whether to the display the IP to the end user or not", + description = "An optional field, whether to the display the IP to the end User or not", authorized = {RoleType.Admin}) private Boolean display; @@ -82,7 +82,7 @@ public class ReserveIPAddrCmd extends BaseCmd implements UserCmd { type = CommandType.UUID, entityType = IPAddressResponse.class, required = true, - description = "the ID of the public IP address to reserve") + description = "The ID of the public IP address to reserve") private Long id; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/address/UpdateIPAddrCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/address/UpdateIPAddrCmd.java index 194967e2e4a4..5e7308685a64 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/address/UpdateIPAddrCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/address/UpdateIPAddrCmd.java @@ -46,14 +46,14 @@ public class UpdateIPAddrCmd extends BaseAsyncCustomIdCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = IPAddressResponse.class, required = true, description = "the ID of the public IP address" + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = IPAddressResponse.class, required = true, description = "The ID of the public IP address" + " to update") private Long id; // unexposed parameter needed for events logging @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, expose = false) private Long ownerId; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the IP to the end user or not", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "An optional field, whether to the display the IP to the end User or not", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/affinitygroup/CreateAffinityGroupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/affinitygroup/CreateAffinityGroupCmd.java index ee0a38ef35dc..9647fd365640 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/affinitygroup/CreateAffinityGroupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/affinitygroup/CreateAffinityGroupCmd.java @@ -42,25 +42,25 @@ public class CreateAffinityGroupCmd extends BaseAsyncCreateCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an account for the affinity group. Must be used with domainId.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "An account for the affinity group. Must be used with domainId.") private String accountName; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, - description = "domainId of the account owning the affinity group", + description = "DomainId of the account owning the affinity group", entityType = DomainResponse.class) private Long domainId; @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, - description = "create affinity group for project") + description = "Create affinity group for project") private Long projectId; - @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "optional description of the affinity group") + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "Optional description of the affinity group") private String description; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "name of the affinity group") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the affinity group") private String affinityGroupName; @Parameter(name = ApiConstants.TYPE, diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/affinitygroup/DeleteAffinityGroupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/affinitygroup/DeleteAffinityGroupCmd.java index 2f24158cadbb..4eace28f5555 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/affinitygroup/DeleteAffinityGroupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/affinitygroup/DeleteAffinityGroupCmd.java @@ -44,12 +44,12 @@ public class DeleteAffinityGroupCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "the account of the affinity group. Must be specified with domain ID") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "The account of the affinity group. Must be specified with domain ID") private String accountName; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, - description = "the domain ID of account owning the affinity group", + description = "The domain ID of account owning the affinity group", entityType = DomainResponse.class) private Long domainId; @@ -63,7 +63,7 @@ public class DeleteAffinityGroupCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the affinity group. Mutually exclusive with ID parameter") private String name; - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, description = "the project of the affinity group", entityType = ProjectResponse.class) + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, description = "The project of the affinity group", entityType = ProjectResponse.class) private Long projectId; ///////////////////////////////////////////////////// @@ -123,7 +123,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Deleting Affinity Group"; + return "Deleting Affinity Group with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/affinitygroup/ListAffinityGroupsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/affinitygroup/ListAffinityGroupsCmd.java index ee23e3794ce1..2f30d8d8eeed 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/affinitygroup/ListAffinityGroupsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/affinitygroup/ListAffinityGroupsCmd.java @@ -36,19 +36,19 @@ public class ListAffinityGroupsCmd extends BaseListProjectAndAccountResourcesCmd //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "lists affinity groups by name") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Lists affinity groups by name") private String affinityGroupName; @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, - description = "lists affinity groups by virtual machine ID", + description = "Lists affinity groups by Instance ID", entityType = UserVmResponse.class) private Long virtualMachineId; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, description = "list the affinity group by the ID provided", entityType = AffinityGroupResponse.class) + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, description = "List the affinity group by the ID provided", entityType = AffinityGroupResponse.class) private Long id; - @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "lists affinity groups by type") + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "Lists affinity groups by type") private String affinityGroupType; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/affinitygroup/UpdateVMAffinityGroupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/affinitygroup/UpdateVMAffinityGroupCmd.java index 6cd9bce62595..7a7e3ee298a9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/affinitygroup/UpdateVMAffinityGroupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/affinitygroup/UpdateVMAffinityGroupCmd.java @@ -46,7 +46,7 @@ import com.cloud.vm.VirtualMachine; @APICommand(name = "updateVMAffinityGroup", - description = "Updates the affinity/anti-affinity group associations of a virtual machine. The VM has to be stopped and restarted for the " + description = "Updates the affinity/anti-affinity group associations of an Instance. The Instance has to be stopped and restarted for the " + "new properties to take effect.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, @@ -61,7 +61,7 @@ public class UpdateVMAffinityGroupCmd extends BaseAsyncCmd implements UserCmd { ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, description = "The ID of the virtual machine") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, description = "The ID of the Instance") private Long id; @ACL @@ -69,8 +69,8 @@ public class UpdateVMAffinityGroupCmd extends BaseAsyncCmd implements UserCmd { 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. " - + "Should be passed only when vm is created from a zone with Basic Network support." + " Mutually exclusive with securitygroupnames parameter") + description = "Comma separated list of affinity groups id that are going to be applied to the Instance. " + + "Should be passed only when Instance is created from a zone with Basic Network support." + " Mutually exclusive with securitygroupnames parameter") private List affinityGroupIdList; @ACL @@ -78,8 +78,8 @@ public class UpdateVMAffinityGroupCmd extends BaseAsyncCmd implements UserCmd { 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." - + " Should be passed only when vm is created from a zone with Basic Network support. " + "Mutually exclusive with securitygroupids parameter") + description = "Comma separated list of affinity groups names that are going to be applied to the Instance." + + " Should be passed only when Instance is created from a zone with Basic Network support. " + "Mutually exclusive with securitygroupids parameter") private List affinityGroupNameList; ///////////////////////////////////////////////////// @@ -141,7 +141,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE throw new InvalidParameterValueException("affinitygroupids parameter or affinitygroupnames parameter must be given"); } - CallContext.current().setEventDetails("VM ID: " + getId()); + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.ID)); UserVm result = _affinityGroupService.updateVMAffinityGroups(getId(), getAffinityGroupIdList()); ArrayList dc = new ArrayList(); dc.add(VMDetails.valueOf("affgrp")); @@ -152,7 +152,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update VM's affinity groups"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update Instance's affinity groups"); } } @@ -163,7 +163,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "updating VM affinity group"; + return "Updating Instance affinity group"; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScalePolicyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScalePolicyCmd.java index a000e265f93b..f4bfcf4f135d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScalePolicyCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScalePolicyCmd.java @@ -37,7 +37,7 @@ import com.cloud.user.Account; @APICommand(name = "createAutoScalePolicy", - description = "Creates an autoscale policy for a provision or deprovision action, the action is taken when the all the conditions evaluates to true for the specified duration. The policy is in effect once it is attached to a autscale vm group.", + description = "Creates an autoscale policy for a provision or deprovision action, the action is taken when the all the conditions evaluates to true for the specified duration. The policy is in effect once it is attached to a autscale Instance group.", responseObject = AutoScalePolicyResponse.class, entityType = {AutoScalePolicy.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -51,25 +51,25 @@ public class CreateAutoScalePolicyCmd extends BaseAsyncCreateCmd { @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, - description = "the name of the autoscale policy", + description = "The name of the autoscale policy", since = "4.18.0") private String name; @Parameter(name = ApiConstants.ACTION, type = CommandType.STRING, required = true, - description = "the action to be executed if all the conditions evaluate to true for the specified duration.") + description = "The action to be executed if all the conditions evaluate to true for the specified duration.") private String action; @Parameter(name = ApiConstants.DURATION, type = CommandType.INTEGER, required = true, - description = "the duration in which the conditions have to be true before action is taken") + description = "The duration in which the conditions have to be true before action is taken") private int duration; @Parameter(name = ApiConstants.QUIETTIME, type = CommandType.INTEGER, - description = "the cool down period in which the policy should not be evaluated after the action has been taken") + description = "The cool down period in which the policy should not be evaluated after the action has been taken") private Integer quietTime; @Parameter(name = ApiConstants.CONDITION_IDS, @@ -77,7 +77,7 @@ public class CreateAutoScalePolicyCmd extends BaseAsyncCreateCmd { collectionType = CommandType.UUID, entityType = ConditionResponse.class, required = true, - description = "the list of IDs of the conditions that are being evaluated on every interval") + description = "The list of IDs of the conditions that are being evaluated on every interval") private List conditionIds; // /////////////////////////////////////////////////// @@ -160,7 +160,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "creating AutoScale Policy"; + return "Creating AutoScale Policy"; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmGroupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmGroupCmd.java index 7c9362d4b691..7c04a4c9d53b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmGroupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmGroupCmd.java @@ -39,7 +39,7 @@ import com.cloud.network.rules.LoadBalancer; @APICommand(name = "createAutoScaleVmGroup", - description = "Creates and automatically starts a virtual machine based on a service offering, disk offering, and template.", + description = "Creates and automatically starts an Instance based on a service offering, disk offering, and Template.", responseObject = AutoScaleVmGroupResponse.class, entityType = {AutoScaleVmGroup.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -55,28 +55,28 @@ public class CreateAutoScaleVmGroupCmd extends BaseAsyncCreateCmd { type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, - description = "the ID of the load balancer rule") + description = "The ID of the load balancer rule") private long lbRuleId; @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, - description = "the name of the autoscale vmgroup", + description = "The name of the autoscale Instance Group", since = "4.18.0") private String name; @Parameter(name = ApiConstants.MIN_MEMBERS, type = CommandType.INTEGER, required = true, - description = "the minimum number of members in the vmgroup, the number of instances in the vm group will be equal to or more than this number.") + description = "The minimum number of members in the Instance Group, the number of Instances in the Instance group will be equal to or more than this number.") private int minMembers; @Parameter(name = ApiConstants.MAX_MEMBERS, type = CommandType.INTEGER, required = true, - description = "the maximum number of members in the vmgroup, The number of instances in the vm group will be equal to or less than this number.") + description = "The maximum number of members in the Instance Group, The number of Instances in the Instance group will be equal to or less than this number.") private int maxMembers; - @Parameter(name = ApiConstants.INTERVAL, type = CommandType.INTEGER, description = "the frequency in which the performance counters to be collected") + @Parameter(name = ApiConstants.INTERVAL, type = CommandType.INTEGER, description = "The frequency in which the performance counters to be collected") private Integer interval; @Parameter(name = ApiConstants.SCALEUP_POLICY_IDS, @@ -84,7 +84,7 @@ public class CreateAutoScaleVmGroupCmd extends BaseAsyncCreateCmd { collectionType = CommandType.UUID, entityType = AutoScalePolicyResponse.class, required = true, - description = "list of scaleup autoscale policies") + description = "List of scaleup autoscale policies") private List scaleUpPolicyIds; @Parameter(name = ApiConstants.SCALEDOWN_POLICY_IDS, @@ -92,17 +92,17 @@ public class CreateAutoScaleVmGroupCmd extends BaseAsyncCreateCmd { collectionType = CommandType.UUID, entityType = AutoScalePolicyResponse.class, required = true, - description = "list of scaledown autoscale policies") + description = "List of scaledown autoscale policies") private List scaleDownPolicyIds; @Parameter(name = ApiConstants.VMPROFILE_ID, type = CommandType.UUID, entityType = AutoScaleVmProfileResponse.class, required = true, - description = "the autoscale profile that contains information about the vms in the vm group.") + description = "The autoscale profile that contains information about the Instances in the Instance group.") private long profileId; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the group to the end user or not", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "An optional field, whether to the display the group to the end user or not", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; // /////////////////////////////////////////////////// @@ -180,12 +180,12 @@ public String getCreateEventType() { @Override public String getCreateEventDescription() { - return "creating AutoScale Vm Group"; + return "Creating AutoScale Instance Group"; } @Override public String getEventDescription() { - return "configuring AutoScale Vm Group. Vm Group Id: " + getEntityId(); + return "Configuring AutoScale Instance Group with ID: " + getEntityId(); } @Override @@ -213,7 +213,7 @@ public void create() throws ResourceAllocationException { setEntityId(result.getId()); setEntityUuid(result.getUuid()); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Autoscale Vm Group"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Autoscale Instance Group"); } } @@ -231,11 +231,11 @@ public void execute() { } } catch (Exception ex) { // TODO what will happen if Resource Layer fails in a step in between - logger.warn("Failed to create autoscale vm group", ex); + logger.warn("Failed to create autoscale Instance group", ex); } finally { if (!success || vmGroup == null) { _autoScaleService.deleteAutoScaleVmGroup(getEntityId(), true); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Autoscale Vm Group"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Autoscale Instance Group"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmProfileCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmProfileCmd.java index 2d84b20c5828..db9688aa09aa 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmProfileCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmProfileCmd.java @@ -44,7 +44,7 @@ import com.cloud.network.as.AutoScaleVmProfile; @APICommand(name = "createAutoScaleVmProfile", - description = "Creates a profile that contains information about the virtual machine which will be provisioned automatically by autoscale feature.", + description = "Creates a profile that contains information about the Instance which will be provisioned automatically by autoscale feature.", responseObject = AutoScaleVmProfileResponse.class, entityType = {AutoScaleVmProfile.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -61,33 +61,33 @@ public class CreateAutoScaleVmProfileCmd extends BaseAsyncCreateCmd { type = CommandType.UUID, entityType = ZoneResponse.class, required = true, - description = "availability zone for the auto deployed virtual machine") + description = "Availability zone for the auto deployed Instance") private Long zoneId; @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, required = true, - description = "the service offering of the auto deployed virtual machine") + description = "The service offering of the auto deployed Instance") private Long serviceOfferingId; @Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, required = true, - description = "the template of the auto deployed virtual machine") + description = "The Template of the auto deployed Instance") private Long templateId; @Parameter(name = ApiConstants.OTHER_DEPLOY_PARAMS, type = CommandType.MAP, - description = "parameters other than zoneId/serviceOfferringId/templateId of the auto deployed virtual machine.\n" + description = "Parameters other than zoneId/serviceOfferringId/templateId of the auto deployed Instance.\n" + "Example: otherdeployparams[0].name=serviceofferingid&otherdeployparams[0].value=a7fb50f6-01d9-11ed-8bc1-77f8f0228926&otherdeployparams[1].name=rootdisksize&otherdeployparams[1].value=10 .\n" + "Possible parameters are \"rootdisksize\", \"diskofferingid\",\"size\", \"securitygroupids\", \"overridediskofferingid\", \"keypairs\", \"affinitygroupids'\" and \"networkids\".") private Map> otherDeployParams; @Parameter(name = ApiConstants.AUTOSCALE_EXPUNGE_VM_GRACE_PERIOD, type = CommandType.INTEGER, - description = "the time allowed for existing connections to get closed before a vm is expunged") + description = "The time allowed for existing connections to get closed before an Instance is expunged") private Integer expungeVmGracePeriod; @Parameter(name = ApiConstants.COUNTERPARAM_LIST, @@ -97,7 +97,7 @@ public class CreateAutoScaleVmProfileCmd extends BaseAsyncCreateCmd { @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. " + + description = "An optional binary data that can be sent to the Instance 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. " + @@ -115,19 +115,19 @@ public class CreateAutoScaleVmProfileCmd extends BaseAsyncCreateCmd { @Parameter(name = ApiConstants.AUTOSCALE_USER_ID, type = CommandType.UUID, entityType = UserResponse.class, - description = "the ID of the user used to launch and destroy the VMs") + description = "The ID of the user used to launch and destroy the Instances") private Long autoscaleUserId; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the profile to the end user or not", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "An optional field, whether to the display the profile to the end user or not", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "account that will own the autoscale VM profile") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "Account that will own the autoscale Instance profile") private String accountName; - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "an optional project for the autoscale VM profile") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "An optional project for the autoscale Instance profile") private Long projectId; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "domain ID of the account owning a autoscale VM profile") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "Domain ID of the Account owning an autoscale Instance profile") private Long domainId; // /////////////////////////////////////////////////// @@ -232,7 +232,7 @@ public static String getResultObjectName() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } @@ -247,7 +247,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "creating AutoScale Vm Profile"; + return "Creating AutoScale Instance Profile"; } @Override @@ -271,7 +271,7 @@ public void create() throws ResourceAllocationException { setEntityId(result.getId()); setEntityUuid(result.getUuid()); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Autoscale Vm Profile"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Autoscale Instance Profile"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateConditionCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateConditionCmd.java index 0ffb9afdac4d..61745ccda7d1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateConditionCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateConditionCmd.java @@ -35,7 +35,7 @@ import com.cloud.exception.ResourceAllocationException; import com.cloud.network.as.Condition; -@APICommand(name = "createCondition", description = "Creates a condition for VM auto scaling", responseObject = ConditionResponse.class, entityType = {Condition.class}, +@APICommand(name = "createCondition", description = "Creates a condition for Instance auto scaling", responseObject = ConditionResponse.class, entityType = {Condition.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateConditionCmd extends BaseAsyncCreateCmd { private static final String s_name = "conditionresponse"; @@ -53,13 +53,13 @@ public class CreateConditionCmd extends BaseAsyncCreateCmd { @Parameter(name = ApiConstants.THRESHOLD, type = CommandType.LONG, required = true, description = "Value for which the Counter will be evaluated with the Operator selected.") private Long threshold; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "the account of the condition. " + "Must be used with the domainId parameter.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "The account of the condition. " + "Must be used with the domainId parameter.") private String accountName; - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "an optional project for condition") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "An optional project for condition") private Long projectId; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "the domain ID of the account.") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "The domain ID of the account.") private Long domainId; // /////////////////////////////////////////////////// @@ -127,7 +127,7 @@ public ApiCommandResourceType getApiResourceType() { @Override public String getEventDescription() { - return "creating a condition"; + return "Creating AutoScale condition"; } @Override @@ -137,7 +137,7 @@ public String getEventType() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DeleteAutoScalePolicyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DeleteAutoScalePolicyCmd.java index cee9460dbe60..45315cc744ca 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DeleteAutoScalePolicyCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DeleteAutoScalePolicyCmd.java @@ -46,7 +46,7 @@ public class DeleteAutoScalePolicyCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = AutoScalePolicyResponse.class, required = true, - description = "the ID of the autoscale policy") + description = "The ID of the autoscale policy") private Long id; // /////////////////////////////////////////////////// @@ -79,19 +79,19 @@ public String getEventType() { @Override public String getEventDescription() { - return "deleting AutoScale Policy: " + getId(); + return "Deleting AutoScale Policy with ID: " + getResourceUuid(ApiConstants.ID); } @Override public void execute() { - CallContext.current().setEventDetails("AutoScale Policy Id: " + getId()); + CallContext.current().setEventDetails("AutoScale Policy ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _autoScaleService.deleteAutoScalePolicy(id); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); setResponseObject(response); } else { - logger.warn("Failed to delete autoscale policy " + getId()); + logger.warn("Failed to delete autoscale policy {}", getId()); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete AutoScale Policy"); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DeleteAutoScaleVmGroupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DeleteAutoScaleVmGroupCmd.java index 6bf2157533e1..99d6d903ba45 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DeleteAutoScaleVmGroupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DeleteAutoScaleVmGroupCmd.java @@ -34,7 +34,7 @@ import com.cloud.network.as.AutoScaleVmGroup; import com.cloud.user.Account; -@APICommand(name = "deleteAutoScaleVmGroup", description = "Deletes a autoscale vm group.", responseObject = SuccessResponse.class, entityType = {AutoScaleVmGroup.class}, +@APICommand(name = "deleteAutoScaleVmGroup", description = "Deletes an autoscale Instance group.", responseObject = SuccessResponse.class, entityType = {AutoScaleVmGroup.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DeleteAutoScaleVmGroupCmd extends BaseAsyncCmd { // /////////////////////////////////////////////////// @@ -46,12 +46,12 @@ public class DeleteAutoScaleVmGroupCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = AutoScaleVmGroupResponse.class, required = true, - description = "the ID of the autoscale group") + description = "The ID of the autoscale group") private Long id; @Parameter(name = ApiConstants.CLEANUP, type = CommandType.BOOLEAN, - description = "true if all VMs have to be cleaned up, false otherwise", + description = "True if all VMs have to be cleaned up, false otherwise", since = "4.18.0") private Boolean cleanup; @@ -89,19 +89,19 @@ public String getEventType() { @Override public String getEventDescription() { - return "deleting autoscale vm group: " + getId(); + return "Deleting AutoScale Instance group with ID: " + getResourceUuid(ApiConstants.ID); } @Override public void execute() { - CallContext.current().setEventDetails("AutoScale Vm Group Id: " + getId()); + CallContext.current().setEventDetails("AutoScale Instance Group ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _autoScaleService.deleteAutoScaleVmGroup(id, getCleanup()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); setResponseObject(response); } else { - logger.warn("Failed to delete autoscale vm group " + getId()); + logger.warn("Failed to delete autoscale Instance group {}", getId()); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete autoscale vm group"); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DeleteAutoScaleVmProfileCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DeleteAutoScaleVmProfileCmd.java index b90f6aa8ffa5..bf1e8ecb1677 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DeleteAutoScaleVmProfileCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DeleteAutoScaleVmProfileCmd.java @@ -34,7 +34,7 @@ import com.cloud.network.as.AutoScaleVmProfile; import com.cloud.user.Account; -@APICommand(name = "deleteAutoScaleVmProfile", description = "Deletes a autoscale vm profile.", responseObject = SuccessResponse.class, entityType = {AutoScaleVmProfile.class}, +@APICommand(name = "deleteAutoScaleVmProfile", description = "Deletes a autoscale Instance profile.", responseObject = SuccessResponse.class, entityType = {AutoScaleVmProfile.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DeleteAutoScaleVmProfileCmd extends BaseAsyncCmd { // /////////////////////////////////////////////////// @@ -46,7 +46,7 @@ public class DeleteAutoScaleVmProfileCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = AutoScaleVmProfileResponse.class, required = true, - description = "the ID of the autoscale profile") + description = "The ID of the autoscale profile") private Long id; // /////////////////////////////////////////////////// @@ -79,19 +79,19 @@ public String getEventType() { @Override public String getEventDescription() { - return "deleting autoscale vm profile: " + getId(); + return "Deleting AutoScale Instance profile with ID: " + getResourceUuid(ApiConstants.ID); } @Override public void execute() { - CallContext.current().setEventDetails("AutoScale VM Profile Id: " + getId()); + CallContext.current().setEventDetails("AutoScale Instance Profile ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _autoScaleService.deleteAutoScaleVmProfile(id); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); setResponseObject(response); } else { - logger.warn("Failed to delete autoscale vm profile " + getId()); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete autoscale vm profile"); + logger.warn("Failed to delete autoscale Instance profile {}", getId()); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete autoscale Instance profile"); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DeleteConditionCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DeleteConditionCmd.java index 9590012e0375..38b77c1553f5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DeleteConditionCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DeleteConditionCmd.java @@ -35,7 +35,7 @@ import com.cloud.network.as.Condition; import com.cloud.user.Account; -@APICommand(name = "deleteCondition", description = "Removes a condition for VM auto scaling", responseObject = SuccessResponse.class, entityType = {Condition.class}, +@APICommand(name = "deleteCondition", description = "Removes a condition for Instance auto scaling", responseObject = SuccessResponse.class, entityType = {Condition.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DeleteConditionCmd extends BaseAsyncCmd { @@ -44,7 +44,7 @@ public class DeleteConditionCmd extends BaseAsyncCmd { // /////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ConditionResponse.class, required = true, description = "the ID of the condition.") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ConditionResponse.class, required = true, description = "The ID of the condition.") private Long id; // /////////////////////////////////////////////////// @@ -64,7 +64,7 @@ public void execute() { SuccessResponse response = new SuccessResponse(getCommandName()); setResponseObject(response); } else { - logger.warn("Failed to delete condition " + getId()); + logger.warn("Failed to delete condition {}", getId()); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete condition."); } } @@ -100,6 +100,6 @@ public String getEventType() { @Override public String getEventDescription() { - return "Deleting a condition."; + return "Deleting AutoScale condition with ID: " + getResourceUuid(ApiConstants.ID); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DisableAutoScaleVmGroupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DisableAutoScaleVmGroupCmd.java index 2414c0d82b62..316fefd62deb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DisableAutoScaleVmGroupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DisableAutoScaleVmGroupCmd.java @@ -33,7 +33,7 @@ import com.cloud.network.as.AutoScaleVmGroup; import com.cloud.user.Account; -@APICommand(name = "disableAutoScaleVmGroup", description = "Disables an AutoScale Vm Group", responseObject = AutoScaleVmGroupResponse.class, entityType = {AutoScaleVmGroup.class}, +@APICommand(name = "disableAutoScaleVmGroup", description = "Disables an AutoScale Instance Group", responseObject = AutoScaleVmGroupResponse.class, entityType = {AutoScaleVmGroup.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DisableAutoScaleVmGroupCmd extends BaseAsyncCmd { private static final String s_name = "disableautoscalevmGroupresponse"; @@ -47,7 +47,7 @@ public class DisableAutoScaleVmGroupCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = AutoScaleVmGroupResponse.class, required = true, - description = "the ID of the autoscale group") + description = "The ID of the autoscale group") private Long id; // /////////////////////////////////////////////////// @@ -62,7 +62,7 @@ public void execute() { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to disable AutoScale Vm Group"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to disable AutoScale Instance Group"); } } @@ -96,7 +96,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Disabling AutoScale Vm Group. Vm Group Id: " + getId(); + return "Disabling AutoScale Instance Group with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/EnableAutoScaleVmGroupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/EnableAutoScaleVmGroupCmd.java index 96d329d3e0c8..8aea4690425e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/EnableAutoScaleVmGroupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/EnableAutoScaleVmGroupCmd.java @@ -33,7 +33,7 @@ import com.cloud.network.as.AutoScaleVmGroup; import com.cloud.user.Account; -@APICommand(name = "enableAutoScaleVmGroup", description = "Enables an AutoScale Vm Group", responseObject = AutoScaleVmGroupResponse.class, entityType = {AutoScaleVmGroup.class}, +@APICommand(name = "enableAutoScaleVmGroup", description = "Enables an AutoScale Instance Group", responseObject = AutoScaleVmGroupResponse.class, entityType = {AutoScaleVmGroup.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class EnableAutoScaleVmGroupCmd extends BaseAsyncCmd { private static final String s_name = "enableautoscalevmGroupresponse"; @@ -47,7 +47,7 @@ public class EnableAutoScaleVmGroupCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = AutoScaleVmGroupResponse.class, required = true, - description = "the ID of the autoscale group") + description = "The ID of the autoscale group") private Long id; // /////////////////////////////////////////////////// @@ -62,7 +62,7 @@ public void execute() { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to enable AutoScale Vm Group"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to enable AutoScale Instance Group"); } } @@ -96,7 +96,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Enabling AutoScale Vm Group. Vm Group Id: " + getId(); + return "Enabling AutoScale Instance Group with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/ListAutoScalePoliciesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/ListAutoScalePoliciesCmd.java index 4935889c5255..5ce0aa7f5b75 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/ListAutoScalePoliciesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/ListAutoScalePoliciesCmd.java @@ -40,21 +40,21 @@ public class ListAutoScalePoliciesCmd extends BaseListProjectAndAccountResources // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AutoScalePolicyResponse.class, description = "the ID of the autoscale policy") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AutoScalePolicyResponse.class, description = "The ID of the autoscale policy") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the autoscale policy", since = "4.18.0") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the autoscale policy", since = "4.18.0") private String name; - @Parameter(name = ApiConstants.CONDITION_ID, type = CommandType.UUID, entityType = ConditionResponse.class, description = "the ID of the condition of the policy") + @Parameter(name = ApiConstants.CONDITION_ID, type = CommandType.UUID, entityType = ConditionResponse.class, description = "The ID of the condition of the policy") private Long conditionId; @Parameter(name = ApiConstants.ACTION, type = CommandType.STRING, - description = "the action to be executed if all the conditions evaluate to true for the specified duration.") + description = "The action to be executed if all the conditions evaluate to true for the specified duration.") private String action; - @Parameter(name = ApiConstants.VMGROUP_ID, type = CommandType.UUID, entityType = AutoScaleVmGroupResponse.class, description = "the ID of the autoscale vm group") + @Parameter(name = ApiConstants.VMGROUP_ID, type = CommandType.UUID, entityType = AutoScaleVmGroupResponse.class, description = "The ID of the autoscale Instance group") private Long vmGroupId; // /////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/ListAutoScaleVmGroupsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/ListAutoScaleVmGroupsCmd.java index 6aa4abcccd8c..4bb0ef091487 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/ListAutoScaleVmGroupsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/ListAutoScaleVmGroupsCmd.java @@ -35,7 +35,7 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.network.as.AutoScaleVmGroup; -@APICommand(name = "listAutoScaleVmGroups", description = "Lists autoscale vm groups.", responseObject = AutoScaleVmGroupResponse.class, entityType = {AutoScaleVmGroup.class}, +@APICommand(name = "listAutoScaleVmGroups", description = "Lists autoscale Instance groups.", responseObject = AutoScaleVmGroupResponse.class, entityType = {AutoScaleVmGroup.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListAutoScaleVmGroupsCmd extends BaseListProjectAndAccountResourcesCmd { @@ -44,25 +44,25 @@ public class ListAutoScaleVmGroupsCmd extends BaseListProjectAndAccountResources // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AutoScaleVmGroupResponse.class, description = "the ID of the autoscale vm group") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AutoScaleVmGroupResponse.class, description = "The ID of the autoscale Instance group") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the autoscale vmgroup", since = "4.18.0") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the autoscale vmgroup", since = "4.18.0") private String name; - @Parameter(name = ApiConstants.LBID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, description = "the ID of the loadbalancer") + @Parameter(name = ApiConstants.LBID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, description = "The ID of the loadbalancer") private Long loadBalancerId; - @Parameter(name = ApiConstants.VMPROFILE_ID, type = CommandType.UUID, entityType = AutoScaleVmProfileResponse.class, description = "the ID of the profile") + @Parameter(name = ApiConstants.VMPROFILE_ID, type = CommandType.UUID, entityType = AutoScaleVmProfileResponse.class, description = "The ID of the profile") private Long profileId; - @Parameter(name = ApiConstants.POLICY_ID, type = CommandType.UUID, entityType = AutoScalePolicyResponse.class, description = "the ID of the policy") + @Parameter(name = ApiConstants.POLICY_ID, type = CommandType.UUID, entityType = AutoScalePolicyResponse.class, description = "The ID of the policy") private Long policyId; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the availability zone ID") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The availability zone ID") private Long zoneId; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; // /////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/ListAutoScaleVmProfilesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/ListAutoScaleVmProfilesCmd.java index bcaea273ce84..4c95e7a4edcd 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/ListAutoScaleVmProfilesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/ListAutoScaleVmProfilesCmd.java @@ -33,7 +33,7 @@ import com.cloud.network.as.AutoScaleVmProfile; -@APICommand(name = "listAutoScaleVmProfiles", description = "Lists autoscale vm profiles.", responseObject = AutoScaleVmProfileResponse.class, entityType = {AutoScaleVmProfile.class}, +@APICommand(name = "listAutoScaleVmProfiles", description = "Lists autoscale Instance profiles.", responseObject = AutoScaleVmProfileResponse.class, entityType = {AutoScaleVmProfile.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListAutoScaleVmProfilesCmd extends BaseListProjectAndAccountResourcesCmd { @@ -42,22 +42,22 @@ public class ListAutoScaleVmProfilesCmd extends BaseListProjectAndAccountResourc // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AutoScaleVmProfileResponse.class, description = "the ID of the autoscale vm profile") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AutoScaleVmProfileResponse.class, description = "The ID of the autoscale Instance profile") private Long id; - @Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "the templateid of the autoscale vm profile") + @Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "The templateid of the autoscale Instance profile") private Long templateId; - @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, description = "list profiles by service offering id", since = "4.4") + @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, description = "List profiles by service offering id", since = "4.4") private Long serviceOffId; - @Parameter(name = ApiConstants.OTHER_DEPLOY_PARAMS, type = CommandType.STRING, description = "the otherdeployparameters of the autoscale vm profile") + @Parameter(name = ApiConstants.OTHER_DEPLOY_PARAMS, type = CommandType.STRING, description = "The otherdeployparameters of the autoscale Instance profile") private String otherDeployParams; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, since = "4.4", description = "availability zone for the auto deployed virtual machine") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, since = "4.4", description = "Availability zone for the auto deployed Instance") private Long zoneId; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; // /////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/ListConditionsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/ListConditionsCmd.java index febf937d75f6..02e54a15ac3a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/ListConditionsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/ListConditionsCmd.java @@ -32,7 +32,7 @@ import com.cloud.network.as.Condition; -@APICommand(name = "listConditions", description = "List Conditions for VM auto scaling", responseObject = ConditionResponse.class, +@APICommand(name = "listConditions", description = "List Conditions for Instance auto scaling", responseObject = ConditionResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListConditionsCmd extends BaseListProjectAndAccountResourcesCmd { @@ -50,7 +50,7 @@ public class ListConditionsCmd extends BaseListProjectAndAccountResourcesCmd { description = "Counter-id of the condition.") private Long counterId; - @Parameter(name = ApiConstants.POLICY_ID, type = CommandType.UUID, entityType = AutoScalePolicyResponse.class, description = "the ID of the policy") + @Parameter(name = ApiConstants.POLICY_ID, type = CommandType.UUID, entityType = AutoScalePolicyResponse.class, description = "The ID of the policy") private Long policyId; // /////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/ListCountersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/ListCountersCmd.java index d03584fd63d5..983c13f07a29 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/ListCountersCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/ListCountersCmd.java @@ -31,7 +31,7 @@ import com.cloud.network.as.Counter; import com.cloud.user.Account; -@APICommand(name = "listCounters", description = "List the counters for VM auto scaling", responseObject = CounterResponse.class, +@APICommand(name = "listCounters", description = "List the counters for Instance auto scaling", responseObject = CounterResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListCountersCmd extends BaseListCmd { private static final String s_name = "counterresponse"; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScalePolicyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScalePolicyCmd.java index 927a9191fcc2..05af6a53a5d3 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScalePolicyCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScalePolicyCmd.java @@ -48,23 +48,23 @@ public class UpdateAutoScalePolicyCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, - description = "the name of the autoscale policy", + description = "The name of the autoscale policy", since = "4.18.0") private String name; - @Parameter(name = ApiConstants.DURATION, type = CommandType.INTEGER, description = "the duration in which the conditions have to be true before action is taken") + @Parameter(name = ApiConstants.DURATION, type = CommandType.INTEGER, description = "The duration in which the conditions have to be true before action is taken") private Integer duration; @Parameter(name = ApiConstants.QUIETTIME, type = CommandType.INTEGER, - description = "the cool down period in which the policy should not be evaluated after the action has been taken") + description = "The cool down period in which the policy should not be evaluated after the action has been taken") private Integer quietTime; @Parameter(name = ApiConstants.CONDITION_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = ConditionResponse.class, - description = "the list of IDs of the conditions that are being evaluated on every interval") + description = "The list of IDs of the conditions that are being evaluated on every interval") private List conditionIds; @ACL(accessType = AccessType.OperateEntry) @@ -72,12 +72,12 @@ public class UpdateAutoScalePolicyCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = AutoScalePolicyResponse.class, required = true, - description = "the ID of the autoscale policy") + description = "The ID of the autoscale policy") private Long id; @Override public void execute() { - CallContext.current().setEventDetails("AutoScale Policy Id: " + getId()); + CallContext.current().setEventDetails("AutoScale Policy ID: " + getResourceUuid(ApiConstants.ID)); AutoScalePolicy result = _autoScaleService.updateAutoScalePolicy(this); if (result != null) { AutoScalePolicyResponse response = _responseGenerator.createAutoScalePolicyResponse(result); @@ -130,7 +130,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Updating Auto Scale Policy. Policy Id: " + getId(); + return "Updating AutoScale Policy with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScaleVmGroupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScaleVmGroupCmd.java index 69ae8aa36aa1..3e13ce10bfb4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScaleVmGroupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScaleVmGroupCmd.java @@ -38,7 +38,7 @@ import com.cloud.network.as.AutoScaleVmGroup; import com.cloud.user.Account; -@APICommand(name = "updateAutoScaleVmGroup", description = "Updates an existing autoscale vm group.", responseObject = AutoScaleVmGroupResponse.class, entityType = {AutoScaleVmGroup.class}, +@APICommand(name = "updateAutoScaleVmGroup", description = "Updates an existing autoscale Instance group.", responseObject = AutoScaleVmGroupResponse.class, entityType = {AutoScaleVmGroup.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class UpdateAutoScaleVmGroupCmd extends BaseAsyncCustomIdCmd { @@ -49,35 +49,35 @@ public class UpdateAutoScaleVmGroupCmd extends BaseAsyncCustomIdCmd { @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, - description = "the name of the autoscale vmgroup", + description = "The name of the autoscale vmgroup", since = "4.18.0") private String name; @Parameter(name = ApiConstants.MIN_MEMBERS, type = CommandType.INTEGER, - description = "the minimum number of members in the vmgroup, the number of instances in the vm group will be equal to or more than this number.") + description = "The minimum number of members in the vmgroup, the number of Instances in the Instance group will be equal to or more than this number.") private Integer minMembers; @Parameter(name = ApiConstants.MAX_MEMBERS, type = CommandType.INTEGER, - description = "the maximum number of members in the vmgroup, The number of instances in the vm group will be equal to or less than this number.") + description = "The maximum number of members in the vmgroup, The number of Instances in the Instance group will be equal to or less than this number.") private Integer maxMembers; - @Parameter(name = ApiConstants.INTERVAL, type = CommandType.INTEGER, description = "the frequency in which the performance counters to be collected") + @Parameter(name = ApiConstants.INTERVAL, type = CommandType.INTEGER, description = "The frequency in which the performance counters to be collected") private Integer interval; @Parameter(name = ApiConstants.SCALEUP_POLICY_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AutoScalePolicyResponse.class, - description = "list of scaleup autoscale policies") + description = "List of scaleup autoscale policies") private List scaleUpPolicyIds; @Parameter(name = ApiConstants.SCALEDOWN_POLICY_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AutoScalePolicyResponse.class, - description = "list of scaledown autoscale policies") + description = "List of scaledown autoscale policies") private List scaleDownPolicyIds; @ACL(accessType = AccessType.OperateEntry) @@ -85,10 +85,10 @@ public class UpdateAutoScaleVmGroupCmd extends BaseAsyncCustomIdCmd { type = CommandType.UUID, entityType = AutoScaleVmGroupResponse.class, required = true, - description = "the ID of the autoscale group") + description = "The ID of the autoscale group") private Long id; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the group to the end user or not", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "An optional field, whether to the display the group to the end user or not", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; // /////////////////////////////////////////////////// @@ -97,7 +97,7 @@ public class UpdateAutoScaleVmGroupCmd extends BaseAsyncCustomIdCmd { @Override public void execute() { - CallContext.current().setEventDetails("AutoScale Vm Group Id: " + getId()); + CallContext.current().setEventDetails("AutoScale Instance Group ID: " + getResourceUuid(ApiConstants.ID)); AutoScaleVmGroup result = _autoScaleService.updateAutoScaleVmGroup(this); if (result != null) { AutoScaleVmGroupResponse response = _responseGenerator.createAutoScaleVmGroupResponse(result); @@ -151,7 +151,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Updating AutoScale Vm Group. Vm Group Id: " + getId(); + return "Updating AutoScale Instance Group with ID: " + getId(); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScaleVmProfileCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScaleVmProfileCmd.java index 0b73fd91b525..9495989df189 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScaleVmProfileCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScaleVmProfileCmd.java @@ -42,7 +42,7 @@ import com.cloud.network.as.AutoScaleVmProfile; import com.cloud.user.Account; -@APICommand(name = "updateAutoScaleVmProfile", description = "Updates an existing autoscale vm profile.", responseObject = AutoScaleVmProfileResponse.class, entityType = {AutoScaleVmProfile.class}, +@APICommand(name = "updateAutoScaleVmProfile", description = "Updates an existing autoscale Instance profile.", responseObject = AutoScaleVmProfileResponse.class, entityType = {AutoScaleVmProfile.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class UpdateAutoScaleVmProfileCmd extends BaseAsyncCustomIdCmd { @@ -56,25 +56,25 @@ public class UpdateAutoScaleVmProfileCmd extends BaseAsyncCustomIdCmd { type = CommandType.UUID, entityType = AutoScaleVmProfileResponse.class, required = true, - description = "the ID of the autoscale vm profile") + description = "The ID of the autoscale Instance profile") private Long id; @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, - description = "the service offering of the auto deployed virtual machine", + description = "The service offering of the auto deployed Instance", since = "4.18.0") private Long serviceOfferingId; @Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, - description = "the template of the auto deployed virtual machine") + description = "The Template of the auto deployed Instance") private Long templateId; @Parameter(name = ApiConstants.AUTOSCALE_EXPUNGE_VM_GRACE_PERIOD, type = CommandType.INTEGER, - description = "the time allowed for existing connections to get closed before a vm is destroyed") + description = "The time allowed for existing connections to get closed before an Instance is destroyed") private Integer expungeVmGracePeriod; @Parameter(name = ApiConstants.COUNTERPARAM_LIST, @@ -84,7 +84,7 @@ public class UpdateAutoScaleVmProfileCmd extends BaseAsyncCustomIdCmd { @Parameter(name = ApiConstants.OTHER_DEPLOY_PARAMS, type = CommandType.MAP, - description = "parameters other than zoneId/serviceOfferringId/templateId of the auto deployed virtual machine. \n" + description = "Parameters other than zoneId/serviceOfferringId/templateId of the auto deployed Instance. \n" + "Example: otherdeployparams[0].name=serviceofferingid&otherdeployparams[0].value=a7fb50f6-01d9-11ed-8bc1-77f8f0228926&otherdeployparams[1].name=rootdisksize&otherdeployparams[1].value=10 .\n" + "Possible parameters are \"rootdisksize\", \"diskofferingid\",\"size\", \"securitygroupids\", \"overridediskofferingid\", \"keypairs\", \"affinitygroupids'\" and \"networkids\".", since = "4.18.0") @@ -92,7 +92,7 @@ public class UpdateAutoScaleVmProfileCmd extends BaseAsyncCustomIdCmd { @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. " + + description = "An optional binary data that can be sent to the Instance 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. " + @@ -112,10 +112,10 @@ public class UpdateAutoScaleVmProfileCmd extends BaseAsyncCustomIdCmd { @Parameter(name = ApiConstants.AUTOSCALE_USER_ID, type = CommandType.UUID, entityType = UserResponse.class, - description = "the ID of the user used to launch and destroy the VMs") + description = "The ID of the user used to launch and destroy the Instances") private Long autoscaleUserId; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the profile to the end user or not", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "An optional field, whether to the display the profile to the end user or not", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; // /////////////////////////////////////////////////// @@ -124,14 +124,14 @@ public class UpdateAutoScaleVmProfileCmd extends BaseAsyncCustomIdCmd { @Override public void execute() { - CallContext.current().setEventDetails("AutoScale Policy Id: " + getId()); + CallContext.current().setEventDetails("AutoScale Policy ID: " + getResourceUuid(ApiConstants.ID)); AutoScaleVmProfile result = _autoScaleService.updateAutoScaleVmProfile(this); if (result != null) { AutoScaleVmProfileResponse response = _responseGenerator.createAutoScaleVmProfileResponse(result); response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update autoscale vm profile"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update autoscale Instance profile"); } } @@ -190,7 +190,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Updating AutoScale Vm Profile. Vm Profile Id: " + getId(); + return "Updating AutoScale Instance Profile with ID: " + getId(); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateConditionCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateConditionCmd.java index 4ed8244ff0c9..43de212da7b9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateConditionCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateConditionCmd.java @@ -36,7 +36,7 @@ import com.cloud.network.as.Condition; import com.cloud.user.Account; -@APICommand(name = "updateCondition", description = "Updates a condition for VM auto scaling", responseObject = SuccessResponse.class, entityType = {Condition.class}, +@APICommand(name = "updateCondition", description = "Updates a condition for Instance auto scaling", responseObject = SuccessResponse.class, entityType = {Condition.class}, authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.18.0") public class UpdateConditionCmd extends BaseAsyncCmd { @@ -46,7 +46,7 @@ public class UpdateConditionCmd extends BaseAsyncCmd { // /////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ConditionResponse.class, required = true, description = "the ID of the condition.") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ConditionResponse.class, required = true, description = "The ID of the condition.") private Long id; @Parameter(name = ApiConstants.RELATIONAL_OPERATOR, type = CommandType.STRING, required = true, description = "Relational Operator to be used with threshold. Valid values are EQ, GT, LT, GE, LE.") @@ -110,6 +110,6 @@ public String getEventType() { @Override public String getEventDescription() { - return "Updating a condition."; + return "Updating Instance AutoScale condition with ID: " + getResourceUuid(ApiConstants.ID); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/AssignVirtualMachineToBackupOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/AssignVirtualMachineToBackupOfferingCmd.java index 0e6b2bc165e4..29b1e740b67e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/AssignVirtualMachineToBackupOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/AssignVirtualMachineToBackupOfferingCmd.java @@ -21,6 +21,7 @@ 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.BaseAsyncCmd; @@ -40,7 +41,7 @@ import com.cloud.exception.ResourceUnavailableException; @APICommand(name = "assignVirtualMachineToBackupOffering", - description = "Assigns a VM to a backup offering", + description = "Assigns an Instance to a backup offering", responseObject = SuccessResponse.class, since = "4.14.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) public class AssignVirtualMachineToBackupOfferingCmd extends BaseAsyncCmd { @@ -56,7 +57,7 @@ public class AssignVirtualMachineToBackupOfferingCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = UserVmResponse.class, required = true, - description = "ID of the virtual machine") + description = "ID of the Instance") private Long vmId; @Parameter(name = ApiConstants.BACKUP_OFFERING_ID, @@ -90,7 +91,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add VM to backup offering"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add Instance to backup offering"); } } catch (Exception e) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); @@ -102,6 +103,16 @@ public long getEntityOwnerId() { return CallContext.current().getCallingAccount().getId(); } + @Override + public Long getApiResourceId() { + return vmId; + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.VirtualMachine; + } + @Override public String getEventType() { return EventTypes.EVENT_VM_BACKUP_OFFERING_ASSIGN; @@ -109,6 +120,6 @@ public String getEventType() { @Override public String getEventDescription() { - return "Assigning VM to backup offering ID: " + offeringId; + return "Assigning Instance with ID " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID) + " to backup offering with ID: " + getResourceUuid(ApiConstants.BACKUP_OFFERING_ID); } } 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 4e9028f52173..5b7bd518df0e 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 @@ -41,7 +41,7 @@ import com.cloud.utils.exception.CloudRuntimeException; @APICommand(name = "createBackup", - description = "Create VM backup", + description = "Create Instance backup", responseObject = SuccessResponse.class, since = "4.14.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) public class CreateBackupCmd extends BaseAsyncCreateCmd { @@ -57,7 +57,7 @@ public class CreateBackupCmd extends BaseAsyncCreateCmd { type = CommandType.UUID, entityType = UserVmResponse.class, required = true, - description = "ID of the VM") + description = "ID of the Instance") private Long vmId; @Parameter(name = ApiConstants.NAME, @@ -75,12 +75,18 @@ public class CreateBackupCmd extends BaseAsyncCreateCmd { @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. " + + description = "Quiesce the instance before checkpointing the disks for backup. Applicable only to NAS and KBOSS backup providers. " + "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; + @Parameter(name = ApiConstants.ISOLATED, + type = CommandType.BOOLEAN, + description = ApiConstants.PARAMETER_DESCRIPTION_ISOLATED_BACKUPS, + since = "4.23.0") + private boolean isolated; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -101,6 +107,10 @@ public Boolean getQuiesceVM() { return quiesceVM; } + public boolean isIsolated() { + return isolated; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -114,7 +124,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new CloudRuntimeException("Error while creating backup of VM"); + throw new CloudRuntimeException("Error while creating backup of Instance"); } } catch (Exception e) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); @@ -123,7 +133,12 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE @Override public ApiCommandResourceType getApiResourceType() { - return ApiCommandResourceType.Backup; + return ApiCommandResourceType.VirtualMachine; + } + + @Override + public Long getApiResourceId() { + return vmId; } @Override @@ -138,7 +153,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Creating backup for VM " + vmId; + return "Creating backup for Instance " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupOfferingCmd.java new file mode 100644 index 000000000000..c5d29b615439 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupOfferingCmd.java @@ -0,0 +1,185 @@ +// 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.backup; + +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 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.BackupOfferingResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.backup.Backup; +import org.apache.cloudstack.backup.BackupManager; +import org.apache.cloudstack.backup.BackupOffering; + + +import javax.inject.Inject; +import java.util.List; + +@APICommand(name = "createBackupOffering", description = "Creates a backup offering", responseObject = BackupOfferingResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}, since = "4.23.0") +public class CreateBackupOfferingCmd extends BaseCmd { + + @Inject + protected BackupManager backupManager; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Backup offering name.", required = true) + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, required = true, + description = "The description of the backup offering") + private String description; + + @Parameter(name = ApiConstants.COMPRESS, type = CommandType.BOOLEAN, description = "Whether the backups should be compressed or not.") + private Boolean compress; + + @Parameter(name = ApiConstants.VALIDATE, type = CommandType.BOOLEAN, description = "Whether the backups should be validated or not.") + private Boolean validate; + + @Parameter(name = ApiConstants.VALIDATION_STEPS, type = CommandType.STRING, description = "Which validation steps should be performed. Accepts a comma-separated list of " + + "steps. Accepted values are: wait_for_boot, screenshot and execute_command.") + private String validationSteps; + + @Parameter(name = ApiConstants.ALLOW_QUICK_RESTORE, type = CommandType.BOOLEAN, description = "Whether quick restore is enabled for the backups or not.") + private Boolean allowQuickRestore; + + @Parameter(name = ApiConstants.ALLOW_EXTRACT_FILE, type = CommandType.BOOLEAN, description = "Whether files may be extracted from backups or not.") + private Boolean allowExtractFile; + + @Parameter(name = ApiConstants.BACKUP_CHAIN_SIZE, type = CommandType.INTEGER, description = "Backup chain size for backups created with this offering.") + private Integer backupChainSize; + + @Parameter(name = ApiConstants.COMPRESSION_LIBRARY, type = CommandType.STRING, description = "Compression library, for offerings that support compression. Accepted values " + + "are zstd and zlib. By default, zstd is used for images that support it. If the image only supports zlib, it will be used regardless of this parameter.") + private String compressionLibrary; + + @Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, entityType = ZoneResponse.class, + description = "Restrict the backup offering to the Zone identified by this ID.", required = true) + private Long zoneId; + + @Parameter(name = ApiConstants.ALLOW_USER_DRIVEN_BACKUPS, type = CommandType.BOOLEAN, + description = "Whether users are allowed to create ad-hoc backups and backup schedules when using this offering.", required = true) + private Boolean userDrivenBackups; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = DomainResponse.class, + description = "Restrict the backup offering to the Domains identified by these IDs.") + private List domainIds; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getName() { + return name; + } + + public boolean isCompress() { + return Boolean.TRUE.equals(compress); + } + + public boolean isValidate() { + return Boolean.TRUE.equals(validate); + } + + public boolean isAllowQuickRestore() { + return Boolean.TRUE.equals(allowQuickRestore); + } + + public boolean isAllowExtractFile() { + return Boolean.TRUE.equals(allowExtractFile); + } + + public Integer getBackupChainSize() { + return backupChainSize; + } + + public Backup.CompressionLibrary getCompressionLibrary() { + if (compressionLibrary == null) { + return null; + } + try { + return Backup.CompressionLibrary.valueOf(compressionLibrary); + } catch (IllegalArgumentException e) { + throw new InvalidParameterValueException(String.format("Invalid compression library, accepted values are zstd and zlib, received [%s].", compressionLibrary)); + } + } + + public String getValidationSteps() { + if (validationSteps == null) { + return Backup.ValidationSteps.screenshot.name(); + } + StringBuilder sb = new StringBuilder(); + for (String step : validationSteps.strip().split(",")) { + try { + Backup.ValidationSteps enumStep = Backup.ValidationSteps.valueOf(step); + sb.append(enumStep.name()); + sb.append(","); + } catch (IllegalArgumentException ex) { + logger.error("Invalid validation step informed [{}].", step, ex); + throw new InvalidParameterValueException(String.format("Invalid validation step [%s] informed. Accepted values are: wait_for_boot, screenshot and execute_command.", step)); + } + } + sb.deleteCharAt(sb.lastIndexOf(",")); + return sb.toString(); + } + + public String getDescription() { + return description; + } + + public Long getZoneId() { + return zoneId; + } + + public List getDomainIds() { + return domainIds; + } + + public Boolean getUserDrivenBackups() { + return userDrivenBackups; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, + NetworkRuleConflictException { + BackupOffering offering = backupManager.createBackupOffering(this); + BackupOfferingResponse response = _responseGenerator.createBackupOfferingResponse(offering); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return 0; + } +} 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 a352c688f127..812d78425e89 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 @@ -21,12 +21,12 @@ 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.BackupResponse; import org.apache.cloudstack.api.response.BackupScheduleResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.backup.BackupManager; @@ -37,8 +37,8 @@ import com.cloud.utils.exception.CloudRuntimeException; @APICommand(name = "createBackupSchedule", - description = "Creates a user-defined VM backup schedule", - responseObject = BackupResponse.class, since = "4.14.0", + description = "Creates a User-defined Instance backup schedule", + responseObject = BackupScheduleResponse.class, since = "4.14.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) public class CreateBackupScheduleCmd extends BaseCmd { @@ -53,19 +53,19 @@ public class CreateBackupScheduleCmd extends BaseCmd { type = CommandType.UUID, entityType = UserVmResponse.class, required = true, - description = "ID of the VM for which schedule is to be defined") + description = "ID of the Instance for which schedule is to be defined") private Long vmId; @Parameter(name = ApiConstants.INTERVAL_TYPE, type = CommandType.STRING, required = true, - description = "valid values are HOURLY, DAILY, WEEKLY, and MONTHLY") + description = "Valid values are HOURLY, DAILY, WEEKLY, and MONTHLY") private String intervalType; @Parameter(name = ApiConstants.SCHEDULE, type = CommandType.STRING, required = true, - description = "custom backup schedule, the format is:" + description = "Custom backup schedule, the format is:" + "for HOURLY MM*, for DAILY MM:HH*, for WEEKLY MM:HH:DD (1-7)*, for MONTHLY MM:HH:DD (1-28)") private String schedule; @@ -82,12 +82,18 @@ public class CreateBackupScheduleCmd extends BaseCmd { @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. " + + description = "Quiesce the Instance before checkpointing the disks for backup. Applicable only to NAS and KBOSS backup providers. " + "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; + @Parameter(name = ApiConstants.ISOLATED, + type = CommandType.BOOLEAN, + description = ApiConstants.PARAMETER_DESCRIPTION_ISOLATED_BACKUPS, + since = "4.23.0") + private boolean isolated; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -116,6 +122,10 @@ public Boolean getQuiesceVM() { return quiesceVM; } + public boolean isIsolated() { + return isolated; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -129,7 +139,7 @@ public void execute() throws ServerApiException { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new CloudRuntimeException("Error while creating backup schedule of VM"); + throw new CloudRuntimeException("Error while creating backup schedule of Instance"); } } catch (Exception e) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); @@ -140,4 +150,14 @@ public void execute() throws ServerApiException { public long getEntityOwnerId() { return CallContext.current().getCallingAccount().getId(); } + + @Override + public Long getApiResourceId() { + return vmId; + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.VirtualMachine; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupCmd.java index 7f0a63ecc912..faaf1735e1e9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupCmd.java @@ -41,7 +41,7 @@ import com.cloud.utils.exception.CloudRuntimeException; @APICommand(name = "deleteBackup", - description = "Delete VM backup", + description = "Delete Instance backup", responseObject = SuccessResponse.class, since = "4.14.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) public class DeleteBackupCmd extends BaseAsyncCmd { @@ -57,13 +57,13 @@ public class DeleteBackupCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = BackupResponse.class, required = true, - description = "id of the VM backup") + description = "ID of the Instance backup") private Long backupId; @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, - description = "force the deletion of backup which removes the entire backup chain but keep VM in Backup Offering", + description = "Force the deletion of backup which removes the entire backup chain but keep Instance in Backup Offering", since = "4.18.0.0") private Boolean forced; @@ -92,7 +92,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new CloudRuntimeException("Error while deleting backup of VM"); + throw new CloudRuntimeException("Error while deleting backup of Instance"); } } catch (Exception e) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); @@ -111,6 +111,6 @@ public String getEventType() { @Override public String getEventDescription() { - return "Deleting backup ID " + backupId; + return "Deleting Backup with ID: " + getResourceUuid(ApiConstants.ID); } } 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 dbfd21d5a56b..8dcf7574aed6 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 @@ -40,7 +40,7 @@ import com.cloud.utils.exception.CloudRuntimeException; @APICommand(name = "deleteBackupSchedule", - description = "Deletes the backup schedule of a VM", + description = "Deletes the backup schedule of a Instance", responseObject = SuccessResponse.class, since = "4.14.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) public class DeleteBackupScheduleCmd extends BaseCmd { @@ -85,7 +85,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new CloudRuntimeException("Failed to delete VM backup schedule"); + throw new CloudRuntimeException("Failed to delete Instance backup schedule"); } } catch (Exception e) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DownloadValidationScreenshotCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DownloadValidationScreenshotCmd.java new file mode 100644 index 000000000000..997d0d24c6f0 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DownloadValidationScreenshotCmd.java @@ -0,0 +1,94 @@ +// 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.backup; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import org.apache.cloudstack.api.ACL; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.BackupResponse; +import org.apache.cloudstack.api.response.ExtractResponse; +import org.apache.cloudstack.backup.Backup; +import org.apache.cloudstack.backup.InternalBackupService; + +import javax.inject.Inject; + +@APICommand(name = "downloadValidationScreenshot", description = "Download validation screenshot of given backup.", + responseObject = ExtractResponse.class, since = "4.23.0", requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false) +public class DownloadValidationScreenshotCmd extends BaseAsyncCmd { + + @Inject + private InternalBackupService internalBackupService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @ACL + @Parameter(name = ApiConstants.BACKUP_ID, type = CommandType.UUID, entityType = BackupResponse.class, required = true, + description = "ID of the backup.") + private Long backupId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getBackupId() { + return backupId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getEventType() { + return EventTypes.EVENT_SCREENSHOT_DOWNLOAD; + } + + @Override + public String getEventDescription() { + Backup backup = _entityMgr.findById(Backup.class, getBackupId()); + if (backup == null) { + throw new InvalidParameterValueException(String.format("Unable to find backup with ID [%s].", getBackupId())); + } + return "Downloading validation screenshot of backup " + backup.getUuid(); + } + + @Override + public void execute() { + ExtractResponse response = internalBackupService.downloadScreenshot(getBackupId()); + response.setResponseName(getCommandName()); + response.setObjectName(getCommandName()); + this.setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + Backup backup = _entityMgr.findById(Backup.class, getBackupId()); + if (backup != null) { + return backup.getAccountId(); + } + + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/FinishBackupChainCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/FinishBackupChainCmd.java new file mode 100644 index 000000000000..575df7ae0831 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/FinishBackupChainCmd.java @@ -0,0 +1,86 @@ +// 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.backup; + +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 com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.api.ACL; +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.SuccessResponse; +import org.apache.cloudstack.api.response.VirtualMachineResponse; +import org.apache.cloudstack.backup.InternalBackupService; + +import javax.inject.Inject; + +@APICommand(name = "finishBackupChain", description = "Finish the backup chain of VM. Currently only has effect on VMs with KBOSS backup offerings.", + responseObject = SuccessResponse.class, since = "4.23.0", requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false) +public class FinishBackupChainCmd extends BaseCmd { + @Inject + private InternalBackupService internalBackupService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @ACL + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = VirtualMachineResponse.class, required = true, + description = "ID of the VM to finish the chain.") + private Long vmId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getVmId() { + return vmId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, + NetworkRuleConflictException { + boolean result = internalBackupService.finishBackupChain(getVmId()); + SuccessResponse response = new SuccessResponse(); + response.setSuccess(result); + response.setResponseName(getCommandName()); + response.setObjectName(getCommandName()); + this.setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + VirtualMachine vm = _entityMgr.findById(VirtualMachine.class, getVmId()); + if (vm != null) { + return vm.getAccountId(); + } + + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupScheduleCmd.java index fa6e3ea5d455..17bd06bfdd4b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupScheduleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupScheduleCmd.java @@ -24,7 +24,7 @@ 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.BaseListProjectAndAccountResourcesCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.BackupScheduleResponse; @@ -39,19 +39,18 @@ import com.cloud.exception.NetworkRuleConflictException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; -import com.cloud.utils.exception.CloudRuntimeException; import java.util.ArrayList; import java.util.List; @APICommand(name = "listBackupSchedule", - description = "List backup schedule of a VM", + description = "List backup schedule of an Instance", responseObject = BackupScheduleResponse.class, since = "4.14.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) -public class ListBackupScheduleCmd extends BaseCmd { +public class ListBackupScheduleCmd extends BaseListProjectAndAccountResourcesCmd { @Inject - private BackupManager backupManager; + BackupManager backupManager; ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// @@ -60,10 +59,16 @@ public class ListBackupScheduleCmd extends BaseCmd { @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, - required = true, - description = "ID of the VM") + description = "ID of the Instance") private Long vmId; + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + entityType = BackupScheduleResponse.class, + description = "the ID of the backup schedule", + since = "4.22.0") + private Long id; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -72,6 +77,10 @@ public Long getVmId() { return vmId; } + public Long getId() { + return id; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -79,19 +88,18 @@ public Long getVmId() { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { try{ - List schedules = backupManager.listBackupSchedule(getVmId()); + List schedules = backupManager.listBackupSchedules(this); ListResponse response = new ListResponse<>(); List scheduleResponses = new ArrayList<>(); + if (!CollectionUtils.isNullOrEmpty(schedules)) { for (BackupSchedule schedule : schedules) { scheduleResponses.add(_responseGenerator.createBackupScheduleResponse(schedule)); } - response.setResponses(scheduleResponses, schedules.size()); - response.setResponseName(getCommandName()); - setResponseObject(response); - } else { - throw new CloudRuntimeException("No backup schedule exists for the VM"); } + response.setResponses(scheduleResponses, schedules.size()); + response.setResponseName(getCommandName()); + setResponseObject(response); } catch (Exception e) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupServiceJobsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupServiceJobsCmd.java new file mode 100644 index 000000000000..39ca444ae2ee --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupServiceJobsCmd.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 org.apache.cloudstack.api.command.user.backup; + +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 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.ServerApiException; +import org.apache.cloudstack.api.response.BackupServiceJobResponse; +import org.apache.cloudstack.api.response.BackupResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ZoneResponse; + +@APICommand(name = "listBackupServiceJobs", description = "List backup service jobs", responseObject = BackupServiceJobResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}, since = "4.23.0") +public class ListBackupServiceJobsCmd extends BaseListCmd { + + @Parameter(name = ApiConstants.ID, type = CommandType.LONG, entityType = BackupServiceJobResponse.class, description = "List only job with given ID.") + private Long id; + + @Parameter(name = ApiConstants.BACKUP_ID, type = CommandType.UUID, entityType = BackupResponse.class, description = "List jobs for the given backup.") + private Long backupId; + + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "List jobs in the given host. When passing this parameter, only jobs that are currently executing will be returned.") + private Long hostId; + + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "List jobs in the given zone.") + private Long zoneId; + + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "List jobs with the given type. Accepted values are StartCompression, FinalizeCompression and " + + "BackupValidation.") + private String type; + + @Parameter(name = ApiConstants.EXECUTING, type = CommandType.BOOLEAN, description = "List executing jobs.") + private Boolean executing; + + @Parameter(name = ApiConstants.SCHEDULED, type = CommandType.BOOLEAN, description = "List scheduled jobs.") + private Boolean scheduled; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public Long getBackupId() { + return backupId; + } + + public Long getHostId() { + return hostId; + } + + public Long getZoneId() { + return zoneId; + } + + public String getType() { + return type; + } + + public boolean getExecuting() { + return Boolean.TRUE.equals(executing); + } + + public boolean getScheduled() { + return Boolean.TRUE.equals(scheduled); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, + NetworkRuleConflictException { + ListResponse response = _queryService.listBackupServiceJobs(this); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } +} 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 0c2b4f9b91ef..35cadd9f2e5e 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 @@ -46,7 +46,7 @@ import com.cloud.utils.Pair; @APICommand(name = "listBackups", - description = "Lists VM backups", + description = "Lists Instance backups", responseObject = BackupResponse.class, since = "4.14.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) public class ListBackupsCmd extends BaseListProjectAndAccountResourcesCmd { @@ -61,19 +61,19 @@ public class ListBackupsCmd extends BaseListProjectAndAccountResourcesCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = BackupResponse.class, - description = "id of the backup") + description = "ID of the backup") private Long id; @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, - description = "id of the VM") + description = "ID of the Instance") private Long vmId; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, - description = "list backups by zone id") + description = "List backups by zone id") private Long zoneId; @Parameter(name = ApiConstants.NAME, @@ -89,6 +89,12 @@ public class ListBackupsCmd extends BaseListProjectAndAccountResourcesCmd { description = "list backups by backup offering") private Long backupOfferingId; + @Parameter(name = ApiConstants.STATUS, + type = CommandType.STRING, + since = "4.23.0", + description = "list backups by status") + private String backupStatus; + @Parameter(name = ApiConstants.LIST_VM_DETAILS, type = CommandType.BOOLEAN, since = "4.21.0", @@ -119,6 +125,10 @@ public Long getZoneId() { return zoneId; } + public String getBackupStatus() { + return backupStatus; + } + public Boolean getListVmDetails() { return listVmDetails; } @@ -148,7 +158,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE Pair, Integer> result = backupManager.listBackups(this); setupResponseBackupList(result.first(), result.second()); } catch (Exception e) { - logger.debug("Exception while listing backups", e); + logger.error("Exception while listing backups", e); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RemoveVirtualMachineFromBackupOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RemoveVirtualMachineFromBackupOfferingCmd.java index c451bff54566..62c2f03d8807 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RemoveVirtualMachineFromBackupOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RemoveVirtualMachineFromBackupOfferingCmd.java @@ -21,6 +21,7 @@ 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.BaseAsyncCmd; @@ -39,7 +40,7 @@ import com.cloud.exception.ResourceUnavailableException; @APICommand(name = "removeVirtualMachineFromBackupOffering", - description = "Removes a VM from any existing backup offering", + description = "Removes an Instance from any existing backup offering", responseObject = SuccessResponse.class, since = "4.14.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) public class RemoveVirtualMachineFromBackupOfferingCmd extends BaseAsyncCmd { @@ -55,12 +56,12 @@ public class RemoveVirtualMachineFromBackupOfferingCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = UserVmResponse.class, required = true, - description = "ID of the virtual machine") + description = "ID of the Instance") private Long vmId; @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, - description = "Whether to force remove VM from the backup offering that may also delete VM backups.") + description = "Whether to force remove Instance from the backup offering that may also delete Instance backups.") private Boolean forced; ///////////////////////////////////////////////////// @@ -87,7 +88,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove VM from backup offering"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove Instance from backup offering"); } } catch (Exception e) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); @@ -99,6 +100,16 @@ public long getEntityOwnerId() { return CallContext.current().getCallingAccount().getId(); } + @Override + public Long getApiResourceId() { + return vmId; + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.VirtualMachine; + } + @Override public String getEventType() { return EventTypes.EVENT_VM_BACKUP_OFFERING_REMOVE; @@ -106,6 +117,6 @@ public String getEventType() { @Override public String getEventDescription() { - return "Removing VM ID" + vmId + " from backup offering"; + return "Removing Instance with ID:" + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID) + " from backup offering"; } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreBackupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreBackupCmd.java index 6d02a084b704..9c67aa6a9f69 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreBackupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreBackupCmd.java @@ -26,6 +26,7 @@ import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.api.response.BackupResponse; import org.apache.cloudstack.backup.BackupManager; @@ -38,9 +39,10 @@ import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.commons.lang3.BooleanUtils; @APICommand(name = "restoreBackup", - description = "Restores an existing stopped or deleted VM using a VM backup", + description = "Restores an existing stopped or deleted Instance using an Instance backup", responseObject = SuccessResponse.class, since = "4.14.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) public class RestoreBackupCmd extends BaseAsyncCmd { @@ -59,6 +61,14 @@ public class RestoreBackupCmd extends BaseAsyncCmd { description = "ID of the backup") private Long backupId; + @Parameter(name = ApiConstants.QUICK_RESTORE, type = CommandType.BOOLEAN, entityType = BackupResponse.class, description = "Whether to use the quick restore process or not. " + + "Currently this parameter is only supported by the KBOSS provider.", since = "4.23.0") + private Boolean quickRestore; + + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "If quickrestore is true, which host to start the VM on;" + + " otherwise, ignored. Currently this parameter is only supported by the KBOSS provider.", since = "4.23.0", authorized = {RoleType.Admin}) + private Long hostId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -67,6 +77,14 @@ public Long getBackupId() { return backupId; } + public boolean isQuickRestore() { + return BooleanUtils.isTrue(quickRestore); + } + + public Long getHostId() { + return hostId; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -74,13 +92,13 @@ public Long getBackupId() { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { try { - boolean result = backupManager.restoreBackup(backupId); + boolean result = backupManager.restoreBackup(backupId, isQuickRestore(), getHostId()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new CloudRuntimeException("Error while restoring VM from backup"); + throw new CloudRuntimeException("Error while restoring Instance from backup"); } } catch (Exception e) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); @@ -99,6 +117,6 @@ public String getEventType() { @Override public String getEventDescription() { - return "Restoring VM from backup: " + backupId; + return "Restoring Instance from backup with ID: " + getResourceUuid(ApiConstants.ID); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreVolumeFromBackupAndAttachToVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreVolumeFromBackupAndAttachToVMCmd.java index 310b0bd9bf13..e05845866c2b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreVolumeFromBackupAndAttachToVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreVolumeFromBackupAndAttachToVMCmd.java @@ -20,12 +20,15 @@ import javax.inject.Inject; import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.ACL; 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.BaseAsyncCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.BackupResponse; @@ -41,7 +44,7 @@ import com.cloud.utils.exception.CloudRuntimeException; @APICommand(name = "restoreVolumeFromBackupAndAttachToVM", - description = "Restore and attach a backed up volume to VM", + description = "Restore and attach a backed up volume to Instance", responseObject = SuccessResponse.class, since = "4.14.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) public class RestoreVolumeFromBackupAndAttachToVMCmd extends BaseAsyncCmd { @@ -53,26 +56,37 @@ public class RestoreVolumeFromBackupAndAttachToVMCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// + @ACL @Parameter(name = ApiConstants.BACKUP_ID, type = CommandType.UUID, entityType = BackupResponse.class, required = true, - description = "ID of the VM backup") + description = "ID of the Instance backup") private Long backupId; + @ACL @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.STRING, required = true, description = "ID of the volume backed up") private String volumeUuid; + @ACL @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, - description = "id of the VM where to attach the restored volume") + description = "ID of the Instance where to attach the restored volume") private Long vmId; + @Parameter(name = ApiConstants.QUICK_RESTORE, type = CommandType.BOOLEAN, description = "Whether to use the quick restore process or not. " + + "Currently this parameter is only supported by the KBOSS provider.", since = "4.23.0") + private Boolean quickRestore; + + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "If quickrestore is true, which host to start the VM on;" + + " otherwise, ignored. Currently this parameter is only supported by the KBOSS provider.", since = "4.23.0", authorized = {RoleType.Admin}) + private Long hostId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -89,6 +103,14 @@ public Long getBackupId() { return backupId; } + public boolean isQuickRestore() { + return org.apache.commons.lang3.BooleanUtils.isTrue(quickRestore); + } + + public Long getHostId() { + return hostId; + } + @Override public long getEntityOwnerId() { return CallContext.current().getCallingAccount().getId(); @@ -101,13 +123,13 @@ public long getEntityOwnerId() { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { try { - boolean result = backupManager.restoreBackupVolumeAndAttachToVM(volumeUuid, backupId, vmId); + boolean result = backupManager.restoreBackupVolumeAndAttachToVM(volumeUuid, backupId, vmId, isQuickRestore(), getHostId()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new CloudRuntimeException("Error restoring volume and attaching to VM"); + throw new CloudRuntimeException("Error restoring volume and attaching to Instance"); } } catch (Exception e) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); @@ -121,6 +143,16 @@ public String getEventType() { @Override public String getEventDescription() { - return "Restoring volume "+ volumeUuid + " from backup " + backupId + " and attaching it to VM " + vmId; + return "Restoring volume "+ volumeUuid + " from backup " + getResourceUuid(ApiConstants.BACKUP_ID) + " and attaching it to Instance " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID); + } + + @Override + public Long getApiResourceId() { + return vmId; + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.VirtualMachine; } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/UpdateBackupScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/UpdateBackupScheduleCmd.java index 47c8dc42facd..f4938ee0f87d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/UpdateBackupScheduleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/UpdateBackupScheduleCmd.java @@ -22,7 +22,7 @@ import org.apache.cloudstack.api.response.BackupResponse; @APICommand(name = "updateBackupSchedule", - description = "Updates a user-defined VM backup schedule", + description = "Updates a User-defined Instance backup schedule", responseObject = BackupResponse.class, since = "4.14.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) public class UpdateBackupScheduleCmd extends CreateBackupScheduleCmd { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/AddBackupRepositoryCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/AddBackupRepositoryCmd.java index 5d0c838bc377..7caa4ce710ff 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/AddBackupRepositoryCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/AddBackupRepositoryCmd.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.user.backup.repository; +import com.cloud.utils.StringUtils; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -63,12 +64,14 @@ public class AddBackupRepositoryCmd extends BaseCmd { type = CommandType.UUID, entityType = ZoneResponse.class, required = true, - description = "ID of the zone where the backup repository is to be added") + description = "ID of the zone where the backup repository is to be added for taking backups") private Long zoneId; @Parameter(name = ApiConstants.CAPACITY_BYTES, type = CommandType.LONG, description = "capacity of this backup repository") private Long capacityBytes; + @Parameter(name = ApiConstants.CROSS_ZONE_INSTANCE_CREATION, type = CommandType.BOOLEAN, description = "backups on this repository can be used to create Instances on all Zones", since = "4.22.0") + private Boolean crossZoneInstanceCreation; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -98,7 +101,7 @@ public String getProvider() { } public String getMountOptions() { - return mountOptions == null ? "" : mountOptions; + return StringUtils.isBlank(mountOptions) ? "" : mountOptions; } public Long getZoneId() { @@ -109,6 +112,10 @@ public Long getCapacityBytes() { return capacityBytes; } + public Boolean crossZoneInstanceCreationEnabled() { + return crossZoneInstanceCreation; + } + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/UpdateBackupRepositoryCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/UpdateBackupRepositoryCmd.java new file mode 100644 index 000000000000..5ffd79e497ef --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/UpdateBackupRepositoryCmd.java @@ -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. + +package org.apache.cloudstack.api.command.user.backup.repository; + +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.BackupRepositoryResponse; +import org.apache.cloudstack.backup.BackupRepository; +import org.apache.cloudstack.backup.BackupRepositoryService; +import org.apache.cloudstack.context.CallContext; + +import javax.inject.Inject; + +@APICommand(name = "updateBackupRepository", + description = "Update a backup repository", + responseObject = BackupRepositoryResponse.class, since = "4.22.0", + authorized = {RoleType.Admin}) +public class UpdateBackupRepositoryCmd extends BaseCmd { + + @Inject + private BackupRepositoryService backupRepositoryService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, required = true, entityType = BackupRepositoryResponse.class, description = "ID of the backup repository") + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "name of the backup repository") + private String name; + + @Parameter(name = ApiConstants.ADDRESS, type = CommandType.STRING, description = "address of the backup repository") + private String address; + + @Parameter(name = ApiConstants.MOUNT_OPTIONS, type = CommandType.STRING, description = "shared storage mount options") + private String mountOptions; + + @Parameter(name = ApiConstants.CROSS_ZONE_INSTANCE_CREATION, type = CommandType.BOOLEAN, description = "backups in this repository can be used to create Instances on all Zones") + private Boolean crossZoneInstanceCreation; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public BackupRepositoryService getBackupRepositoryService() { + return backupRepositoryService; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getAddress() { + return address; + } + + public String getMountOptions() { + return mountOptions == null ? "" : mountOptions; + } + + public Boolean crossZoneInstanceCreationEnabled() { + return crossZoneInstanceCreation; + } + + ///////////////////////////////////////////////////// + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + try { + BackupRepository result = backupRepositoryService.updateBackupRepository(this); + if (result != null) { + BackupRepositoryResponse response = _responseGenerator.createBackupRepositoryResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update the backup repository"); + } + } catch (Exception ex4) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex4.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/bucket/CreateBucketCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/bucket/CreateBucketCmd.java index 722556b8e2de..099b56368676 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/bucket/CreateBucketCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/bucket/CreateBucketCmd.java @@ -150,7 +150,7 @@ public ApiCommandResourceType getApiResourceType() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } @@ -181,7 +181,7 @@ public void create() throws ResourceAllocationException { @Override public void execute() { - CallContext.current().setEventDetails("Bucket Id: " + getEntityUuid()); + CallContext.current().setEventDetails("Bucket ID: " + getEntityUuid()); Bucket bucket; try { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/bucket/DeleteBucketCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/bucket/DeleteBucketCmd.java index 8cd2790e4ae2..abbb1760f9d2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/bucket/DeleteBucketCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/bucket/DeleteBucketCmd.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.user.bucket; import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.ResourceAllocationException; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.storage.object.Bucket; import com.cloud.user.Account; @@ -82,8 +83,8 @@ public ApiCommandResourceType getApiResourceType() { } @Override - public void execute() throws ConcurrentOperationException { - CallContext.current().setEventDetails("Bucket Id: " + this._uuidMgr.getUuid(Bucket.class, getId())); + public void execute() throws ConcurrentOperationException, ResourceAllocationException { + CallContext.current().setEventDetails("Bucket ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _bucketService.deleteBucket(id, CallContext.current().getCallingAccount()); SuccessResponse response = new SuccessResponse(getCommandName()); response.setSuccess(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/bucket/UpdateBucketCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/bucket/UpdateBucketCmd.java index f913373c04b6..dc873c300497 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/bucket/UpdateBucketCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/bucket/UpdateBucketCmd.java @@ -113,7 +113,7 @@ public ApiCommandResourceType getApiResourceType() { @Override public void execute() throws ConcurrentOperationException { - CallContext.current().setEventDetails("Bucket Id: " + this._uuidMgr.getUuid(Bucket.class, getId())); + CallContext.current().setEventDetails("Bucket ID: " + getResourceUuid(ApiConstants.ID)); boolean result = false; try { result = _bucketService.updateBucket(this, CallContext.current().getCallingAccount()); 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 e73bb97a21ba..2cb64070950b 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 @@ -21,7 +21,9 @@ 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.CapabilitiesResponse; +import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.config.ApiServiceConfiguration; import com.cloud.user.Account; @@ -30,12 +32,22 @@ requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListCapabilitiesCmd extends BaseCmd { + @Parameter(name = ApiConstants.DOMAIN_ID, + type = CommandType.UUID, + entityType = DomainResponse.class, + description = "the domain for listing capabilities.", + since = "4.23.0") + private Long domainId; @Override public long getEntityOwnerId() { return Account.ACCOUNT_ID_SYSTEM; } + public Long getDomainId() { + return domainId; + } + @Override public void execute() { Map capabilities = _mgr.listCapabilities(this); @@ -51,6 +63,7 @@ public void execute() { response.setDiskOffMaxSize((Long)capabilities.get("customDiskOffMaxSize")); response.setRegionSecondaryEnabled((Boolean)capabilities.get("regionSecondaryEnabled")); response.setKVMSnapshotEnabled((Boolean)capabilities.get("KVMSnapshotEnabled")); + response.setSnapshotShowChainSize((Boolean)capabilities.get("SnapshotShowChainSize")); response.setAllowUserViewDestroyedVM((Boolean)capabilities.get("allowUserViewDestroyedVM")); response.setAllowUserExpungeRecoverVM((Boolean)capabilities.get("allowUserExpungeRecoverVM")); response.setAllowUserExpungeRecoverVolume((Boolean)capabilities.get("allowUserExpungeRecoverVolume")); @@ -75,6 +88,11 @@ public void execute() { 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.setAdditionalConfigEnabled((Boolean) capabilities.get(ApiConstants.ADDITONAL_CONFIG_ENABLED)); + if (capabilities.containsKey(ApiConstants.VPN_CUSTOMER_GATEWAY_PARAMETERS)) { + Map vpnCustomerGatewayParameters = (Map) capabilities.get(ApiConstants.VPN_CUSTOMER_GATEWAY_PARAMETERS); + response.setVpnCustomerGatewayParameters(vpnCustomerGatewayParameters); + } response.setObjectName("capability"); response.setResponseName(getCommandName()); this.setResponseObject(response); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/consoleproxy/CreateConsoleEndpointCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/consoleproxy/CreateConsoleEndpointCmd.java index 63b47e163b6b..41eaf36e4252 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/consoleproxy/CreateConsoleEndpointCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/consoleproxy/CreateConsoleEndpointCmd.java @@ -35,11 +35,12 @@ import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.utils.consoleproxy.ConsoleAccessUtils; import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.ObjectUtils; import javax.inject.Inject; import java.util.Map; -@APICommand(name = "createConsoleEndpoint", description = "Create a console endpoint to connect to a VM console", +@APICommand(name = "createConsoleEndpoint", description = "Create a console endpoint to connect to a Instance console", responseObject = CreateConsoleEndpointResponse.class, since = "4.18.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) @@ -53,7 +54,7 @@ public class CreateConsoleEndpointCmd extends BaseCmd { type = CommandType.UUID, entityType = UserVmResponse.class, required = true, - description = "ID of the VM") + description = "ID of the Instance") private Long vmId; @Parameter(name = ApiConstants.TOKEN, @@ -70,7 +71,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE CreateConsoleEndpointResponse response = createResponse(endpoint); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Unable to generate console endpoint for vm " + vmId); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Unable to generate console endpoint for Instance " + vmId); } } @@ -86,6 +87,10 @@ private CreateConsoleEndpointResponse createResponse(ConsoleEndpoint endpoint) { } private ConsoleEndpointWebsocketResponse createWebsocketResponse(ConsoleEndpoint endpoint) { + if (ObjectUtils.allNull(endpoint.getWebsocketHost(), endpoint.getWebsocketPort(), endpoint.getWebsocketPath(), + endpoint.getWebsocketToken(), endpoint.getWebsocketExtra())) { + return null; + } ConsoleEndpointWebsocketResponse wsResponse = new ConsoleEndpointWebsocketResponse(); wsResponse.setHost(endpoint.getWebsocketHost()); wsResponse.setPort(endpoint.getWebsocketPort()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/AddDnsServerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/AddDnsServerCmd.java new file mode 100644 index 000000000000..298ddd64a31c --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/AddDnsServerCmd.java @@ -0,0 +1,169 @@ +// 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.dns; + +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.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.DnsServerResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.dns.DnsProviderType; +import org.apache.cloudstack.dns.DnsServer; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.BooleanUtils; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.utils.EnumUtils; + +@APICommand(name = "addDnsServer", + description = "Adds a new external DNS server", + responseObject = DnsServerResponse.class, + entityType = {DnsServer.class}, + requestHasSensitiveInfo = true, + responseHasSensitiveInfo = false, + since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class AddDnsServerCmd extends BaseCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + /// + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the DNS server") + private String name; + + @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, description = "API URL of the provider") + private String url; + + @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, required = true, description = "Provider type (e.g., PowerDNS)") + private String provider; + + @Parameter(name = ApiConstants.DNS_USER_NAME, type = CommandType.STRING, + description = "Username or email associated with the DNS provider account (used for authentication)") + private String dnsUserName; + + @Parameter(name = ApiConstants.DNS_API_KEY, required = true, type = CommandType.STRING, description = "API key or token for the DNS provider") + private String dnsApiKey; + + @Parameter(name = ApiConstants.PORT, type = CommandType.INTEGER, description = "Port number of the external DNS server") + private Integer port; + + @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, + description = "Whether this DNS server can be used by accounts other than the owner to create and manage DNS zones") + private Boolean isPublic; + + @Parameter(name = ApiConstants.PUBLIC_DOMAIN_SUFFIX, type = CommandType.STRING, + description = "Domain suffix that restricts DNS zones created by non-owner accounts to subdomains of this " + + "suffix (for example, sub.example.com under example.com)") + private String publicDomainSuffix; + + @Parameter(name = ApiConstants.NAME_SERVERS, type = CommandType.LIST, collectionType = CommandType.STRING, + required = true, + description = "Comma separated list of name servers; used to create NS records for the DNS Zone (for example, ns1.example.com, ns2.example.com)") + private List nameServers; + + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "Details in key/value pairs using " + + "format details[i].keyname=keyvalue. Example: details[0].pdnsServerId=localhost") + protected Map details; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getName() { return name; } + + public String getUrl() { return url; } + + public String getDnsApiKey() { + return dnsApiKey; + } + + public Integer getPort() { + return port; + } + + public Boolean isPublic() { + return BooleanUtils.isTrue(isPublic); + } + + public String getPublicDomainSuffix() { + return publicDomainSuffix; + } + + public List getNameServers() { + return nameServers; + } + + public DnsProviderType getProvider() { + DnsProviderType dnsProviderType = EnumUtils.getEnumIgnoreCase(DnsProviderType.class, provider, DnsProviderType.PowerDNS); + if (dnsProviderType == null) { + throw new InvalidParameterValueException(String.format("Invalid value passed for provider type, valid values are: %s", + EnumUtils.listValues(DnsProviderType.values()))); + } + return dnsProviderType; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + @Override + public void execute() { + try { + DnsServer server = dnsProviderManager.addDnsServer(this); + if (server != null) { + DnsServerResponse response = dnsProviderManager.createDnsServerResponse(server); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add DNS server"); + } + } catch (Exception ex) { + logger.error("Failed to add DNS server", ex); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + } + + public String getDnsUserName() { + return dnsUserName; + } + + public Map getDetails() { + Map detailsMap = new HashMap<>(); + if (MapUtils.isNotEmpty(details)) { + Collection props = details.values(); + for (Object prop : props) { + HashMap detail = (HashMap) prop; + for (Map.Entry entry: detail.entrySet()) { + detailsMap.put(entry.getKey(),entry.getValue()); + } + } + } + return detailsMap; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/AssociateDnsZoneToNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/AssociateDnsZoneToNetworkCmd.java new file mode 100644 index 000000000000..5ffffc7c304d --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/AssociateDnsZoneToNetworkCmd.java @@ -0,0 +1,94 @@ +// 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.dns; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.acl.SecurityChecker; +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.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DnsZoneNetworkMapResponse; +import org.apache.cloudstack.api.response.DnsZoneResponse; +import org.apache.cloudstack.api.response.NetworkResponse; + +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.network.Network; +import com.cloud.user.Account; + +@APICommand(name = "associateDnsZoneToNetwork", + description = "Associates a DNS Zone with a Network for VM auto-registration", + responseObject = DnsZoneNetworkMapResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class AssociateDnsZoneToNetworkCmd extends BaseCmd { + + @Parameter(name = ApiConstants.DNS_ZONE_ID, type = CommandType.UUID, entityType = DnsZoneResponse.class, + required = true, description = "The ID of the DNS zone") + private Long dnsZoneId; + + @ACL(accessType = SecurityChecker.AccessType.OperateEntry) + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, + required = true, description = "The ID of the network") + private Long networkId; + + @Parameter(name = "subdomain", type = CommandType.STRING, + description = "Optional subdomain to append (e.g., 'dev' creates vm1.dev.example.com)") + private String subDomain; + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + DnsZoneNetworkMapResponse response = dnsProviderManager.associateZoneToNetwork(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + Network network = _entityMgr.findById(Network.class, networkId); + if (network != null) { + return network.getAccountId(); + } + return Account.ACCOUNT_ID_SYSTEM; + } + + public Long getDnsZoneId() { + return dnsZoneId; + } + + public Long getNetworkId() { + return networkId; + } + + public String getSubDomain() { + return subDomain; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/CreateDnsRecordCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/CreateDnsRecordCmd.java new file mode 100644 index 000000000000..d2fe3a5024a7 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/CreateDnsRecordCmd.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.user.dns; + +import java.util.List; + +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.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DnsRecordResponse; +import org.apache.cloudstack.api.response.DnsZoneResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.dns.DnsRecord; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.utils.EnumUtils; + +@APICommand(name = "createDnsRecord", + description = "Creates a DNS record directly on the provider", + responseObject = DnsRecordResponse.class, + entityType = {DnsRecord.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class CreateDnsRecordCmd extends BaseAsyncCmd { + + @ACL + @Parameter(name = ApiConstants.DNS_ZONE_ID, type = CommandType.UUID, entityType = DnsZoneResponse.class, required = true, + description = "ID of the DNS zone") + private Long dnsZoneId; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "DNS record name") + private String name; + + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, required = true, description = "DNS record type (e.g., A, AAAA, CNAME, MX, TXT, etc.)") + private String type; + + @Parameter(name = ApiConstants.CONTENTS, type = CommandType.LIST, collectionType = CommandType.STRING, required = true, + description = "The content of the record (IP address for A/AAAA, FQDN for CNAME/NS, quoted string for TXT, etc.)") + private List contents; + + @Parameter(name = "ttl", type = CommandType.INTEGER, description = "Time to live") + private Integer ttl; + + // Getters + public Long getDnsZoneId() { return dnsZoneId; } + public String getName() { return name; } + + public List getContents() { return contents; } + public Integer getTtl() { return (ttl == null) ? 3600 : ttl; } + + public DnsRecord.RecordType getType() { + DnsRecord.RecordType dnsRecordType = EnumUtils.getEnumIgnoreCase(DnsRecord.RecordType.class, type); + if (dnsRecordType == null) { + throw new InvalidParameterValueException("Invalid value passed for record type, valid values are: " + EnumUtils.listValues(DnsRecord.RecordType.values())); + } + return dnsRecordType; + } + + @Override + public void execute() { + try { + DnsRecordResponse response = dnsProviderManager.createDnsRecord(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { return CallContext.current().getCallingAccount().getId(); } + + @Override + public String getEventType() { return EventTypes.EVENT_DNS_RECORD_CREATE; } + + @Override + public String getEventDescription() { return "Creating DNS Record: " + getName(); } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/CreateDnsZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/CreateDnsZoneCmd.java new file mode 100644 index 000000000000..10a706e3a815 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/CreateDnsZoneCmd.java @@ -0,0 +1,154 @@ +// 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.dns; + +import java.util.Arrays; + +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.BaseAsyncCreateCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DnsServerResponse; +import org.apache.cloudstack.api.response.DnsZoneResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.dns.DnsZone; +import org.apache.commons.lang3.StringUtils; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.utils.EnumUtils; + +@APICommand(name = "createDnsZone", + description = "Creates a new DNS Zone on a specific server", + responseObject = DnsZoneResponse.class, + entityType = {DnsZone.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class CreateDnsZoneCmd extends BaseAsyncCreateCmd { + + ///////////////////////////////////////////////////// + //////////////// API Parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, + description = "The name of the DNS zone (e.g. example.com)") + private String name; + + @ACL + @Parameter(name = ApiConstants.DNS_SERVER_ID, type = CommandType.UUID, entityType = DnsServerResponse.class, + required = true, description = "The ID of the DNS server to host this zone") + private Long dnsServerId; + + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, + description = "The type of zone (Public, Private). Defaults to Public.") + private String type; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "The description of the DNS zone") + private String description; + + @Parameter(name = ApiConstants.EXISTING, type = CommandType.BOOLEAN, entityType = DnsZoneResponse.class, + description = "If true, imports an existing DNS zone from the DNS provider into CloudStack. " + + "If false, creates the zone in the DNS provider and registers it in CloudStack. Default is false") + private Boolean existing = false; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getName() { + return name; + } + + public Long getDnsServerId() { + return dnsServerId; + } + + public DnsZone.ZoneType getType() { + if (StringUtils.isBlank(type)) { + return DnsZone.ZoneType.Public; + } + DnsZone.ZoneType zoneType = EnumUtils.getEnumIgnoreCase(DnsZone.ZoneType.class, type); + if (zoneType == null) { + throw new IllegalArgumentException("Invalid type value, supported values are: " + Arrays.toString(DnsZone.ZoneType.values())); + } + return zoneType; + } + + public String getDescription() { + return description; + } + + ///////////////////////////////////////////////////// + /////////////// Implementation ////////////////////// + ///////////////////////////////////////////////////// + + @Override + public void create() throws ResourceAllocationException { + try { + DnsZone zone = dnsProviderManager.allocateDnsZone(this); + if (zone != null) { + setEntityId(zone.getId()); + setEntityUuid(zone.getUuid()); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create DNS Zone entity"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to allocate DNS Zone: " + e.getMessage()); + } + } + + @Override + public void execute() { + try { + DnsZone result = dnsProviderManager.provisionDnsZone(getEntityId(), isExistingZone()); + if (result != null) { + DnsZoneResponse response = dnsProviderManager.createDnsZoneResponse(result); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to provision DNS Zone on external provider"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to provision DNS Zone: " + e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_DNS_ZONE_CREATE; + } + + @Override + public String getEventDescription() { + return "creating DNS zone: " + getName(); + } + + public Boolean isExistingZone() { + return Boolean.TRUE.equals(existing); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsRecordCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsRecordCmd.java new file mode 100644 index 000000000000..1d7914c346e9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsRecordCmd.java @@ -0,0 +1,92 @@ +// 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.dns; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.acl.SecurityChecker; +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.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DnsZoneResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.dns.DnsRecord; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.utils.EnumUtils; + +@APICommand(name = "deleteDnsRecord", + description = "Deletes a DNS record from the external provider", + responseObject = SuccessResponse.class, + entityType = {DnsRecord.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class DeleteDnsRecordCmd extends BaseAsyncCmd { + + @ACL(accessType = SecurityChecker.AccessType.OperateEntry) + @Parameter(name = ApiConstants.DNS_ZONE_ID, type = CommandType.UUID, entityType = DnsZoneResponse.class, + required = true, description = "The ID of the DNS zone") + private Long dnsZoneId; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true) + private String name; + + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, required = true) + private String type; + + // Getters + public DnsRecord.RecordType getType() { + DnsRecord.RecordType dnsRecordType = EnumUtils.getEnumIgnoreCase(DnsRecord.RecordType.class, type); + if (dnsRecordType == null) { + throw new InvalidParameterValueException("Invalid value passed for record type, valid values are: " + EnumUtils.listValues(DnsRecord.RecordType.values())); + } + return dnsRecordType; + } + public Long getDnsZoneId() { return dnsZoneId; } + public String getName() { return name; } + + @Override + public void execute() { + try { + boolean result = dnsProviderManager.deleteDnsRecord(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete DNS Record"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Error deleting DNS Record: " + e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { return CallContext.current().getCallingAccount().getId(); } + + @Override + public String getEventType() { return EventTypes.EVENT_DNS_RECORD_DELETE; } + + @Override + public String getEventDescription() { return "Deleting DNS Record: " + getName(); } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsServerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsServerCmd.java new file mode 100644 index 000000000000..099fc62f354c --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsServerCmd.java @@ -0,0 +1,115 @@ +// 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.dns; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.acl.SecurityChecker; +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.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DnsServerResponse; +import org.apache.cloudstack.api.response.DnsZoneResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.dns.DnsServer; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; + +@APICommand(name = "deleteDnsServer", + description = "Removes a DNS server integration", + responseObject = SuccessResponse.class, + entityType = {DnsServer.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class DeleteDnsServerCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API Parameters ///////////////////// + ///////////////////////////////////////////////////// + + @ACL(accessType = SecurityChecker.AccessType.OperateEntry) + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DnsServerResponse.class, + required = true, description = "the ID of the DNS server") + private Long id; + + @Parameter(name = ApiConstants.CLEANUP, type = CommandType.BOOLEAN, + entityType = DnsZoneResponse.class, description = "If true, all associated DNS zones will be cleaned up " + + "when the server is removed. Default: true") + private Boolean cleanup = true; + + @Parameter(name = ApiConstants.UNMANAGE, type = CommandType.BOOLEAN, entityType = DnsZoneResponse.class, + description = "If true, the DNS zone is only removed from CloudStack (unmanaged); if false, it is removed " + + "from both CloudStack and the DNS provider. Default: false") + private Boolean unmanage = false; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// Implementation ////////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + try { + boolean result = dnsProviderManager.deleteDnsServer(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete DNS server"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete DNS server: " + e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + DnsServer server = _entityMgr.findById(DnsServer.class, id); + if (server != null) { + return server.getAccountId(); + } + // If server not found, return System to fail safely (or let manager handle 404) + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public String getEventType() { return EventTypes.EVENT_DNS_SERVER_DELETE; } + + @Override + public String getEventDescription() { return "Deleting DNS server ID: " + getId(); } + + public Boolean getCleanup() { + return Boolean.TRUE.equals(cleanup); + } + + public Boolean isUnmanage() { + return Boolean.TRUE.equals(unmanage); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsZoneCmd.java new file mode 100644 index 000000000000..a6d5a710ae56 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsZoneCmd.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.user.dns; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.acl.SecurityChecker; +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.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DnsZoneResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.dns.DnsZone; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; + +@APICommand(name = "deleteDnsZone", + description = "Removes a DNS Zone from CloudStack and the external provider", + responseObject = SuccessResponse.class, + entityType = {DnsZone.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class DeleteDnsZoneCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API Parameters ///////////////////// + ///////////////////////////////////////////////////// + + @ACL(accessType = SecurityChecker.AccessType.OperateEntry) + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DnsZoneResponse.class, required = true, + description = "The ID of the DNS zone") + private Long id; + + @Parameter(name = ApiConstants.UNMANAGE, type = CommandType.BOOLEAN, entityType = DnsZoneResponse.class, + description = "If true, removes the DNS zone only from CloudStack; if false, removes it from " + + "both CloudStack and the DNS provider. Default: false") + private Boolean unmanage = false; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation ////////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + try { + boolean result = dnsProviderManager.deleteDnsZone(getId(), isUnmanage()); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete DNS Zone"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + DnsZone zone = _entityMgr.findById(DnsZone.class, id); + if (zone != null) { + return zone.getAccountId(); + } + // Fallback or System if not found (likely to fail in execute() anyway) + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_DNS_ZONE_DELETE; + } + + @Override + public String getEventDescription() { + return "Deleting DNS Zone ID: " + getId(); + } + + public Boolean isUnmanage() { + return Boolean.TRUE.equals(unmanage); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/DisassociateDnsZoneFromNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/DisassociateDnsZoneFromNetworkCmd.java new file mode 100644 index 000000000000..b0aa0902e9e6 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/DisassociateDnsZoneFromNetworkCmd.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.dns; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.acl.SecurityChecker; +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.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + +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.network.Network; +import com.cloud.user.Account; + +@APICommand(name = "disassociateDnsZoneFromNetwork", + description = "Removes the association between a DNS Zone and a Network", + responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class DisassociateDnsZoneFromNetworkCmd extends BaseCmd { + + @ACL(accessType = SecurityChecker.AccessType.OperateEntry) + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, + required = true, description = "The ID of the Network") + private Long networkId; + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + boolean result = dnsProviderManager.disassociateZoneFromNetwork(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to disassociate DNS zone from network."); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + Network network = _entityMgr.findById(Network.class, networkId); + if (network != null) { + return network.getAccountId(); + } + return Account.ACCOUNT_ID_SYSTEM; + } + + public Long getNetworkId() { + return networkId; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/ListDnsProvidersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/ListDnsProvidersCmd.java new file mode 100644 index 000000000000..800f030754b9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/ListDnsProvidersCmd.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.api.command.user.dns; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.response.DnsProviderResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.dns.DnsProvider; + +@APICommand(name = "listDnsProviders", + description = "Lists available DNS plugin providers", + responseObject = DnsProviderResponse.class, + entityType = {DnsProvider.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class ListDnsProvidersCmd extends BaseListCmd { + + @Override + public void execute() { + List providers = dnsProviderManager.listProviderNames(); + ListResponse response = new ListResponse<>(); + List responses = new ArrayList<>(); + for (String name : providers) { + DnsProviderResponse resp = new DnsProviderResponse(name); + resp.setName(name); + responses.add(resp); + } + response.setResponses(responses); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/ListDnsRecordsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/ListDnsRecordsCmd.java new file mode 100644 index 000000000000..246c8bee9aab --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/ListDnsRecordsCmd.java @@ -0,0 +1,54 @@ +// 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.dns; + +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.DnsRecordResponse; +import org.apache.cloudstack.api.response.DnsZoneResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.dns.DnsRecord; + +@APICommand(name = "listDnsRecords", + description = "Lists DNS records from the external provider", + responseObject = DnsRecordResponse.class, + entityType = {DnsRecord.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class ListDnsRecordsCmd extends BaseListCmd { + + @Parameter(name = ApiConstants.DNS_ZONE_ID, type = CommandType.UUID, entityType = DnsZoneResponse.class, required = true, + description = "ID of the DNS zone to list records from") + private Long dnsZoneId; + + public Long getDnsZoneId() { + return dnsZoneId; + } + + @Override + public void execute() { + // The manager will fetch live data from the plugin + ListResponse response = dnsProviderManager.listDnsRecords(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/ListDnsServersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/ListDnsServersCmd.java new file mode 100644 index 000000000000..ca7ac1944a9d --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/ListDnsServersCmd.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.dns; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListAccountResourcesCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.DnsServerResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.dns.DnsProviderType; +import org.apache.cloudstack.dns.DnsServer; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.utils.EnumUtils; + +@APICommand(name = "listDnsServers", + description = "Lists DNS servers owned by the account.", + responseObject = DnsServerResponse.class, + entityType = {DnsServer.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class ListDnsServersCmd extends BaseListAccountResourcesCmd { + + ///////////////////////////////////////////////////// + //////////////// API Parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DnsServerResponse.class, + description = "the ID of the DNS server") + private Long id; + + @Parameter(name = ApiConstants.PROVIDER_TYPE, type = CommandType.STRING, + description = "filter by provider type (e.g. PowerDNS, Cloudflare)") + private String providerType; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public DnsProviderType getProviderType() { + DnsProviderType dnsProviderType = EnumUtils.getEnumIgnoreCase(DnsProviderType.class, providerType, DnsProviderType.PowerDNS); + if (dnsProviderType == null) { + throw new InvalidParameterValueException(String.format("Invalid value passed for provider type, valid values are: %s", + EnumUtils.listValues(DnsProviderType.values()))); + } + return dnsProviderType; + } + + ///////////////////////////////////////////////////// + /////////////// Implementation ////////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + ListResponse response = dnsProviderManager.listDnsServers(this); + response.setResponseName(getCommandName()); + response.setObjectName("dnsserver"); + setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/ListDnsZonesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/ListDnsZonesCmd.java new file mode 100644 index 000000000000..e71bdabaf617 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/ListDnsZonesCmd.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.dns; + +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.DnsServerResponse; +import org.apache.cloudstack.api.response.DnsZoneResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.dns.DnsZone; + +@APICommand(name = "listDnsZones", + description = "Lists DNS zones.", responseObject = DnsZoneResponse.class, + entityType = {DnsZone.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class ListDnsZonesCmd extends BaseListCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DnsZoneResponse.class, + description = "List DNS zone by ID") + private Long id; + + @Parameter(name = "dnsserverid", type = CommandType.UUID, entityType = DnsServerResponse.class, + description = "List DNS zones belonging to a specific DNS server") + private Long dnsServerId; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "List by zone name") + private String name; + + public Long getId() { return id; } + public Long getDnsServerId() { return dnsServerId; } + public String getName() { return name; } + + @Override + public void execute() { + ListResponse response = dnsProviderManager.listDnsZones(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/UpdateDnsServerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/UpdateDnsServerCmd.java new file mode 100644 index 000000000000..6b790fa8ade8 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/UpdateDnsServerCmd.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 org.apache.cloudstack.api.command.user.dns; + +import java.util.List; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.acl.SecurityChecker; +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.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DnsServerResponse; +import org.apache.cloudstack.dns.DnsServer; +import org.apache.commons.lang3.StringUtils; + +import com.cloud.user.Account; +import com.cloud.utils.EnumUtils; + +@APICommand(name = "updateDnsServer", + description = "Update DNS server", + responseObject = DnsServerResponse.class, + entityType = {DnsServer.class}, + requestHasSensitiveInfo = true, responseHasSensitiveInfo = false, + since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class UpdateDnsServerCmd extends BaseCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @ACL(accessType = SecurityChecker.AccessType.OperateEntry) + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DnsServerResponse.class, + required = true, description = "The ID of the DNS server to update") + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Name of the DNS server") + private String name; + + @Parameter(name = ApiConstants.URL, type = CommandType.STRING, description = "API URL of the provider") + private String url; + + @Parameter(name = ApiConstants.DNS_API_KEY, type = CommandType.STRING, description = "API Key or Credentials for the external provider") + private String dnsApiKey; + + @Parameter(name = ApiConstants.PORT, type = CommandType.INTEGER, description = "Port number of the external DNS server") + private Integer port; + + @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, + description = "Whether this DNS server can be used by accounts other than the owner to create and manage DNS zones") + private Boolean isPublic; + + @Parameter(name = ApiConstants.PUBLIC_DOMAIN_SUFFIX, type = CommandType.STRING, + description = "Domain suffix that restricts DNS zones created by non-owner accounts to subdomains of this " + + "suffix (for example, sub.example.com under example.com)") + private String publicDomainSuffix; + + @Parameter(name = ApiConstants.NAME_SERVERS, type = CommandType.LIST, collectionType = CommandType.STRING, + description = "Comma separated list of name servers; used to create NS records for the DNS Zone (for example, ns1.example.com, ns2.example.com)") + private List nameServers; + + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "Update state for the DNS server (Enabled, Disabled)") + private String state; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { return id; } + public String getName() { return name; } + public String getUrl() { return url; } + public String getDnsApiKey() { + return dnsApiKey; + } + public Integer getPort() { + return port; + } + public Boolean isPublic() { + return isPublic; + } + public String getPublicDomainSuffix() { + return publicDomainSuffix; + } + public String getNameServers() { + if (nameServers == null) { + return null; + } + return StringUtils.join(nameServers.stream() + .filter(StringUtils::isNotBlank) + .map(StringUtils::trim) + .toArray(String[]::new), ","); + } + + @Override + public long getEntityOwnerId() { + DnsServer server = _entityMgr.findById(DnsServer.class, id); + if (server != null) { + return server.getAccountId(); + } + // If server not found, return System to fail safely (or let manager handle 404) + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() { + try { + DnsServer server = dnsProviderManager.updateDnsServer(this); + if (server != null) { + DnsServerResponse response = dnsProviderManager.createDnsServerResponse(server); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update DNS server"); + } + } catch (Exception ex) { + logger.error("Failed to update DNS server", ex); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + } + + public DnsServer.State getState() { + if (StringUtils.isBlank(state)) { + return null; + } + DnsServer.State dnsState = EnumUtils.getEnumIgnoreCase(DnsServer.State.class, state); + if (dnsState == null) { + throw new IllegalArgumentException("Invalid state value: " + state); + } + return dnsState; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/UpdateDnsZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/UpdateDnsZoneCmd.java new file mode 100644 index 000000000000..5952b73ea6e4 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/UpdateDnsZoneCmd.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.command.user.dns; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.acl.SecurityChecker; +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.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DnsZoneResponse; +import org.apache.cloudstack.dns.DnsZone; + +import com.cloud.user.Account; + +@APICommand(name = "updateDnsZone", + description = "Updates a DNS Zone's metadata", + responseObject = DnsZoneResponse.class, + entityType = {DnsZone.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class UpdateDnsZoneCmd extends BaseCmd { + + ///////////////////////////////////////////////////// + //////////////// API Parameters ///////////////////// + ///////////////////////////////////////////////////// + + @ACL(accessType = SecurityChecker.AccessType.OperateEntry) + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DnsZoneResponse.class, + required = true, description = "The ID of the DNS zone") + private Long id; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "The description of the DNS zone to be updated") + private String description; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getDescription() { + return description; + } + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// Implementation ////////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + try { + DnsZone result = dnsProviderManager.updateDnsZone(this); + if (result != null) { + DnsZoneResponse response = dnsProviderManager.createDnsZoneResponse(result); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update DNS Zone on external provider"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update DNS Zone: " + e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + DnsZone dnsZone = _entityMgr.findById(DnsZone.class, id); + if (dnsZone != null) { + return dnsZone.getAccountId(); + } + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/event/ArchiveEventsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/event/ArchiveEventsCmd.java index 669b1782e9e4..e449f4b002bc 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/event/ArchiveEventsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/event/ArchiveEventsCmd.java @@ -48,18 +48,18 @@ public class ArchiveEventsCmd extends BaseCmd { type = CommandType.LIST, collectionType = CommandType.UUID, entityType = EventResponse.class, - description = "the IDs of the events") + description = "The IDs of the events") private List ids; - @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, description = "end date range to archive events" + @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, description = "End date range to archive events" + " (including) this date (use format \"yyyy-MM-dd\" or the new format \"yyyy-MM-ddThh:mm:ss\")") private Date endDate; - @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, description = "start date range to archive events" + @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, description = "Start date range to archive events" + " (including) this date (use format \"yyyy-MM-dd\" or the new format \"yyyy-MM-ddThh:mm:ss\")") private Date startDate; - @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "archive by event type") + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "Archive by event type") private String type; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/event/DeleteEventsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/event/DeleteEventsCmd.java index c9c3f1d69554..db183a08e5ab 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/event/DeleteEventsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/event/DeleteEventsCmd.java @@ -48,18 +48,18 @@ public class DeleteEventsCmd extends BaseCmd { type = CommandType.LIST, collectionType = CommandType.UUID, entityType = EventResponse.class, - description = "the IDs of the events") + description = "The IDs of the events") private List ids; - @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, description = "end date range to delete events" + @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, description = "End date range to delete events" + " (including) this date (use format \"yyyy-MM-dd\" or the new format \"yyyy-MM-ddThh:mm:ss\")") private Date endDate; - @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, description = "start date range to delete events" + @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, description = "Start date range to delete events" + " (including) this date (use format \"yyyy-MM-dd\" or the new format \"yyyy-MM-ddThh:mm:ss\")") private Date startDate; - @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "delete by event type") + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "Delete by event type") private String type; // /////////////////////////////////////////////////// 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 cb935c13e97b..b86d1f8b956a 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 @@ -36,41 +36,41 @@ public class ListEventsCmd extends BaseListProjectAndAccountResourcesCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = EventResponse.class, description = "the ID of the event") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = EventResponse.class, description = "The ID of the event") private Long id; - @Parameter(name = ApiConstants.DURATION, type = CommandType.INTEGER, description = "the duration of the event") + @Parameter(name = ApiConstants.DURATION, type = CommandType.INTEGER, description = "The duration of the event") private Integer duration; @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, - description = "the end date range of the list you want to retrieve (use format \"yyyy-MM-dd\" or the new format \"yyyy-MM-dd HH:mm:ss\")") + description = "The end date range of the list you want to retrieve (use format \"yyyy-MM-dd\" or the new format \"yyyy-MM-dd HH:mm:ss\")") private Date endDate; - @Parameter(name = ApiConstants.ENTRY_TIME, type = CommandType.INTEGER, description = "the time the event was entered") + @Parameter(name = ApiConstants.ENTRY_TIME, type = CommandType.INTEGER, description = "The time the event was entered") private Integer entryTime; - @Parameter(name = ApiConstants.LEVEL, type = CommandType.STRING, description = "the event level (INFO, WARN, ERROR)") + @Parameter(name = ApiConstants.LEVEL, type = CommandType.STRING, description = "The event level (INFO, WARN, ERROR)") private String level; @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, - description = "the start date range of the list you want to retrieve (use format \"yyyy-MM-dd\" or the new format \"yyyy-MM-dd HH:mm:ss\")") + description = "The start date range of the list you want to retrieve (use format \"yyyy-MM-dd\" or the new format \"yyyy-MM-dd HH:mm:ss\")") private Date startDate; - @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "the event type (see event types)") + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "The event type (see event types)") private String type; - @Parameter(name = ApiConstants.START_ID, type = CommandType.UUID, entityType = EventResponse.class, description = "the parent/start ID of the event, when provided this will list all the events with the start/parent ID including the parent event") + @Parameter(name = ApiConstants.START_ID, type = CommandType.UUID, entityType = EventResponse.class, description = "The parent/start ID of the event, when provided this will list all the events with the start/parent ID including the parent event") private Long startId; - @Parameter(name = ApiConstants.RESOURCE_ID, type = CommandType.STRING, description = "the ID of the resource associated with the event", since="4.17.0") + @Parameter(name = ApiConstants.RESOURCE_ID, type = CommandType.STRING, description = "The ID of the resource associated with the event", since="4.17.0") private String resourceId; - @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, description = "the type of the resource associated with the event", since="4.17.0") + @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, description = "The type of the resource associated with the event", since="4.17.0") private String resourceType; - @Parameter(name = ApiConstants.ARCHIVED, type = CommandType.BOOLEAN, description = "true to list archived events otherwise false", since="4.19.0") + @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") diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/CreateEgressFirewallRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/CreateEgressFirewallRuleCmd.java index 8cbbcea6a59f..e2c84ac85c7a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/CreateEgressFirewallRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/CreateEgressFirewallRuleCmd.java @@ -57,37 +57,37 @@ public class CreateEgressFirewallRuleCmd extends BaseAsyncCreateCmd implements F type = CommandType.UUID, entityType = NetworkResponse.class, required = true, - description = "the network id of the port forwarding rule") + description = "The Network ID of the port forwarding rule") private Long networkId; @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, required = true, - description = "the protocol for the firewall rule. Valid values are TCP/UDP/ICMP.") + description = "The protocol for the firewall rule. Valid values are TCP/UDP/ICMP.") private String protocol; - @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "the starting port of firewall rule") + @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "The starting port of firewall rule") private Integer publicStartPort; - @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "the ending port of firewall rule") + @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "The ending port of firewall rule") private Integer publicEndPort; - @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the cidr list to forward traffic from. Multiple entries must be separated by a single comma character (,).") + @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "The CIDR list to forward traffic from. Multiple entries must be separated by a single comma character (,).") private List cidrlist; - @Parameter(name = ApiConstants.DEST_CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the cidr list to forward traffic to. Multiple entries must be separated by a single comma character (,).") + @Parameter(name = ApiConstants.DEST_CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "The CIDR list to forward traffic to. Multiple entries must be separated by a single comma character (,).") private List destCidrList; - @Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "type of the icmp message being sent") + @Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "Type of the ICMP message being sent") private Integer icmpType; - @Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "error code for this icmp message") + @Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "Error code for this ICMP message") private Integer icmpCode; - @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "type of firewallrule: system/user") + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "Type of firewallrule: system/user") private String type; - @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}) + @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}) private Boolean display; // /////////////////////////////////////////////////// @@ -147,7 +147,7 @@ public void execute() throws ResourceUnavailableException { boolean success = false; FirewallRule rule = _entityMgr.findById(FirewallRule.class, getEntityId()); try { - CallContext.current().setEventDetails("Rule Id: " + getEntityId()); + CallContext.current().setEventDetails("Rule ID: " + getEntityUuid()); success = _firewallService.applyEgressFirewallRules(rule, callerContext.getCallingAccount()); // State is different after the rule is applied, so get new object here rule = _entityMgr.findById(FirewallRule.class, getEntityId()); @@ -212,7 +212,7 @@ public State getState() { } @Override - public long getNetworkId() { + public Long getNetworkId() { return networkId; } @@ -255,11 +255,8 @@ public void create() { } } catch (NetworkRuleConflictException ex) { String message = "Network rule conflict: "; - if (!logger.isTraceEnabled()) { - logger.info(message + ex.getMessage()); - } else { - logger.trace(message, ex); - } + logger.error("{}{}", message, ex.getMessage()); + logger.trace(message, ex); throw new ServerApiException(ApiErrorCode.NETWORK_RULE_CONFLICT_ERROR, ex.getMessage()); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmd.java index efccb5c09b0d..30dd1a2d015a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmd.java @@ -57,34 +57,34 @@ public class CreateFirewallRuleCmd extends BaseAsyncCreateCmd implements Firewal type = CommandType.UUID, entityType = IPAddressResponse.class, required = true, - description = "the IP address id of the port forwarding rule") + description = "The IP address ID of the port forwarding rule") private Long ipAddressId; @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, required = true, - description = "the protocol for the firewall rule. Valid values are TCP/UDP/ICMP.") + description = "The protocol for the firewall rule. Valid values are TCP/UDP/ICMP.") private String protocol; - @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "the starting port of firewall rule") + @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "The starting port of firewall rule") private Integer publicStartPort; - @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "the ending port of firewall rule") + @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "The ending port of firewall rule") private Integer publicEndPort; - @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the CIDR list to forward traffic from. Multiple entries must be separated by a single comma character (,).") + @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "The CIDR list to forward traffic from. Multiple entries must be separated by a single comma character (,).") private List cidrlist; - @Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "type of the ICMP message being sent") + @Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "Type of the ICMP message being sent") private Integer icmpType; - @Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "error code for this icmp message") + @Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "Error code for this icmp message") private Integer icmpCode; - @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "type of firewallrule: system/user") + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "Type of firewallrule: system/user") private String type; - @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}) + @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}) private Boolean display; // /////////////////////////////////////////////////// @@ -150,7 +150,7 @@ public void execute() throws ResourceUnavailableException { boolean success = false; FirewallRule rule = _entityMgr.findById(FirewallRule.class, getEntityId()); try { - CallContext.current().setEventDetails("Rule ID: " + getEntityId()); + CallContext.current().setEventDetails("Rule ID: " + getEntityUuid()); success = _firewallService.applyIngressFwRules(rule.getSourceIpAddressId(), callerContext.getCallingAccount()); // State is different after the rule is applied, so get new object here @@ -171,7 +171,7 @@ public void execute() throws ResourceUnavailableException { @Override public long getId() { - throw new UnsupportedOperationException("database ID can only provided by VO objects"); + throw new UnsupportedOperationException("Database ID can only provided by VO objects"); } @Override @@ -223,13 +223,9 @@ public State getState() { } @Override - public long getNetworkId() { - IpAddress ip = _entityMgr.findById(IpAddress.class, getIpAddressId()); - Long ntwkId = null; - - if (ip.getAssociatedWithNetworkId() != null) { - ntwkId = ip.getAssociatedWithNetworkId(); - } + public Long getNetworkId() { + IpAddress ip = getIp(); + Long ntwkId = isVpcIp(ip) ? getVpcNetworkIdForFirewallRule(ip) : getIsolatedNetworkIdForFirewallRule(ip); if (ntwkId == null) { throw new InvalidParameterValueException("Unable to create firewall rule for the IP address ID=" + ipAddressId + @@ -238,6 +234,12 @@ public long getNetworkId() { return ntwkId; } + @Override + public Long getVpcId() { + IpAddress ip = getIp(); + return isVpcIp(ip) ? ip.getVpcId() : null; + } + @Override public long getEntityOwnerId() { Account account = CallContext.current().getCallingAccount(); @@ -271,7 +273,7 @@ public void create() { setEntityUuid(result.getUuid()); } } catch (NetworkRuleConflictException ex) { - logger.trace("Network Rule Conflict: ", ex); + logger.error("Network Rule Conflict: ", ex); throw new ServerApiException(ApiErrorCode.NETWORK_RULE_CONFLICT_ERROR, ex.getMessage(), ex); } } @@ -300,7 +302,21 @@ public String getSyncObjType() { @Override public Long getSyncObjId() { - return getIp().getAssociatedWithNetworkId(); + Long syncObjId = getIp().getAssociatedWithNetworkId(); + return syncObjId != null ? syncObjId : getNetworkId(); + } + + private boolean isVpcIp(IpAddress ip) { + return ip.getVpcId() != null; + } + + private Long getIsolatedNetworkIdForFirewallRule(IpAddress ip) { + return ip.getAssociatedWithNetworkId(); + } + + private Long getVpcNetworkIdForFirewallRule(IpAddress ip) { + // VPC flow is independent from tier association; manager resolves execution network. + return ip.getNetworkId(); } private IpAddress getIp() { @@ -311,6 +327,7 @@ private IpAddress getIp() { return ip; } + @Override public Integer getIcmpCode() { if (icmpCode != null) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/CreatePortForwardingRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/CreatePortForwardingRuleCmd.java index 9d1fe176019d..66fc118395ea 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/CreatePortForwardingRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/CreatePortForwardingRuleCmd.java @@ -54,7 +54,6 @@ requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements PortForwardingRule { - // /////////////////////////////////////////////////// // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// @@ -64,37 +63,37 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P type = CommandType.UUID, entityType = IPAddressResponse.class, required = true, - description = "the IP address id of the port forwarding rule") + description = "The IP address ID of the port forwarding rule") private Long ipAddressId; @Parameter(name = ApiConstants.PRIVATE_START_PORT, type = CommandType.INTEGER, required = true, - description = "the starting port of port forwarding rule's private port range") + description = "The starting port of port forwarding rule's private port range") private Integer privateStartPort; @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, required = true, - description = "the protocol for the port forwarding rule. Valid values are TCP or UDP.") + description = "The protocol for the port forwarding rule. Valid values are TCP or UDP.") private String protocol; @Parameter(name = ApiConstants.PRIVATE_END_PORT, type = CommandType.INTEGER, required = false, - description = "the ending port of port forwarding rule's private port range") + description = "The ending port of port forwarding rule's private port range") private Integer privateEndPort; @Parameter(name = ApiConstants.PUBLIC_START_PORT, type = CommandType.INTEGER, required = true, - description = "the starting port of port forwarding rule's public port range") + description = "The starting port of port forwarding rule's public port range") private Integer publicStartPort; @Parameter(name = ApiConstants.PUBLIC_END_PORT, type = CommandType.INTEGER, required = false, - description = "the ending port of port forwarding rule's private port range") + description = "The ending port of port forwarding rule's private port range") private Integer publicEndPort; @ACL(accessType = AccessType.OperateEntry) @@ -102,17 +101,17 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P type = CommandType.UUID, entityType = UserVmResponse.class, required = true, - description = "the ID of the virtual machine for the port forwarding rule") + description = "The ID of the Instance for the port forwarding rule") private Long virtualMachineId; @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, - description = " the source CIDR list to allow traffic from; all other CIDRs will be blocked. " + + description = "The source CIDR list to allow traffic from; all other CIDRs will be blocked. " + "Multiple entries must be separated by a single comma character (,). This param will be used only for VPC tiers. By default, all CIDRs are allowed.") private List sourceCidrList; - @Parameter(name = ApiConstants.OPEN_FIREWALL, type = CommandType.BOOLEAN, description = "if true, firewall rule for source/end public port is automatically created; " + @Parameter(name = ApiConstants.OPEN_FIREWALL, type = CommandType.BOOLEAN, description = "If true, firewall rule for source/end public port is automatically created; " + "if false - firewall rule has to be created explicitly. If not specified 1) defaulted to false when PF" + " rule is being created for VPC guest network 2) in all other cases defaulted to true") private Boolean openFirewall; @@ -120,16 +119,16 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, - description = "the network of the virtual machine the port forwarding rule will be created for. " + description = "The Network of the Instance the port forwarding 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.VM_GUEST_IP, type = CommandType.STRING, required = false, - description = "VM guest nic secondary IP address for the port forwarding rule") + description = "Instance guest NIC secondary IP address for the port forwarding rule") private String vmSecondaryIp; - @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}) + @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}) private Boolean display; // /////////////////////////////////////////////////// @@ -177,7 +176,7 @@ public Boolean getOpenFirewall() { } } - private Long getVpcId() { + public Long getVpcId() { if (ipAddressId != null) { IpAddress ipAddr = _networkService.getIp(ipAddressId); if (ipAddr == null || !ipAddr.readyToUse()) { @@ -199,7 +198,7 @@ public void execute() throws ResourceUnavailableException { boolean success = true; PortForwardingRule rule = null; try { - CallContext.current().setEventDetails("Rule Id: " + getEntityId()); + CallContext.current().setEventDetails("Rule ID: " + getEntityUuid()); if (getOpenFirewall()) { success = success && _firewallService.applyIngressFirewallRules(ipAddressId, callerContext.getCallingAccount()); @@ -276,15 +275,9 @@ public State getState() { } @Override - public long getNetworkId() { + public Long getNetworkId() { IpAddress ip = _entityMgr.findById(IpAddress.class, getIpAddressId()); - Long ntwkId = null; - - if (ip.getAssociatedWithNetworkId() != null) { - ntwkId = ip.getAssociatedWithNetworkId(); - } else { - ntwkId = networkId; - } + Long ntwkId = _networkService.getPreferredNetworkIdForPublicIpRuleAssignment(ip, networkId); if (ntwkId == null) { throw new InvalidParameterValueException("Unable to create port forwarding rule for the ipAddress id=" + ipAddressId + " as ip is not associated with any network and no networkId is passed in"); @@ -335,7 +328,7 @@ public void create() { Ip privateIp = getVmSecondaryIp(); if (privateIp != null) { if (!NetUtils.isValidIp4(privateIp.toString())) { - throw new InvalidParameterValueException("Invalid vm ip address"); + throw new InvalidParameterValueException("Invalid Instance IP address"); } } @@ -359,7 +352,7 @@ public String getEventType() { @Override public String getEventDescription() { IpAddress ip = _networkService.getIp(ipAddressId); - return ("Applying port forwarding rule for Ip: " + ip.getAddress() + " with virtual machine:" + virtualMachineId); + return ("Applying port forwarding rule for Ip: " + ip.getAddress() + " with Instance:" + virtualMachineId); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/DeleteEgressFirewallRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/DeleteEgressFirewallRuleCmd.java index b93d943eab9f..4b606683a396 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/DeleteEgressFirewallRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/DeleteEgressFirewallRuleCmd.java @@ -46,7 +46,7 @@ public class DeleteEgressFirewallRuleCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "the ID of the firewall rule") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "The ID of the firewall rule") private Long id; // unexposed parameter needed for events logging @@ -71,7 +71,7 @@ public String getEventType() { @Override public String getEventDescription() { - return ("Deleting egress firewall rule id=" + id); + return ("Deleting egress firewall rule with ID: " + getResourceUuid(ApiConstants.ID)); } @Override @@ -89,7 +89,7 @@ public long getEntityOwnerId() { @Override public void execute() throws ResourceUnavailableException { - CallContext.current().setEventDetails("Rule Id: " + id); + CallContext.current().setEventDetails("Rule ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _firewallService.revokeEgressFirewallRule(id, true); if (result) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/DeleteFirewallRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/DeleteFirewallRuleCmd.java index c4a4dfd75cb1..ff2dce8dacf0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/DeleteFirewallRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/DeleteFirewallRuleCmd.java @@ -44,7 +44,7 @@ public class DeleteFirewallRuleCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "the ID of the firewall rule") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "The ID of the firewall rule") private Long id; // unexposed parameter needed for events logging @@ -69,7 +69,7 @@ public String getEventType() { @Override public String getEventDescription() { - return ("Deleting firewall rule ID=" + id); + return ("Deleting firewall rule with ID:" + getResourceUuid(ApiConstants.ID)); } @Override @@ -87,7 +87,7 @@ public long getEntityOwnerId() { @Override public void execute() throws ResourceUnavailableException { - CallContext.current().setEventDetails("Rule Id: " + id); + CallContext.current().setEventDetails("Rule ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _firewallService.revokeIngressFwRule(id, true); if (result) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/DeletePortForwardingRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/DeletePortForwardingRuleCmd.java index 267d18d8a8a7..d0b607d7af48 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/DeletePortForwardingRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/DeletePortForwardingRuleCmd.java @@ -48,7 +48,7 @@ public class DeletePortForwardingRuleCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, - description = "the ID of the port forwarding rule") + description = "The ID of the port forwarding rule") private Long id; // unexposed parameter needed for events logging @@ -73,7 +73,7 @@ public String getEventType() { @Override public String getEventDescription() { - return ("Deleting port forwarding rule for ID=" + id); + return "Deleting port forwarding rule with ID:" + getResourceUuid(ApiConstants.ID); } @Override @@ -92,7 +92,7 @@ public long getEntityOwnerId() { @Override public void execute() { - CallContext.current().setEventDetails("Rule ID: " + id); + CallContext.current().setEventDetails("Rule ID: " + getResourceUuid(ApiConstants.ID)); //revoke corresponding firewall rule first boolean result = _firewallService.revokeRelatedFirewallRule(id, true); result = result && _rulesService.revokePortForwardingRule(id, true); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/ListEgressFirewallRulesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/ListEgressFirewallRulesCmd.java index aa0fd28fdec8..4a1547cccaee 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/ListEgressFirewallRulesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/ListEgressFirewallRulesCmd.java @@ -48,16 +48,16 @@ public class ListEgressFirewallRulesCmd extends BaseListTaggedResourcesCmd imple @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, - description = "the network ID for the egress firewall services") + description = "The network ID for the egress firewall services") private Long networkId; @Parameter(name = ApiConstants.IP_ADDRESS_ID, type = CommandType.UUID, entityType = IPAddressResponse.class, - description = "the ID of IP address of the firewall services") + description = "The ID of IP address of the firewall services") private Long ipAddressId; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/ListFirewallRulesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/ListFirewallRulesCmd.java index 19a05b158908..6cb560df8f0a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/ListFirewallRulesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/ListFirewallRulesCmd.java @@ -47,17 +47,17 @@ public class ListFirewallRulesCmd extends BaseListTaggedResourcesCmd implements @Parameter(name = ApiConstants.IP_ADDRESS_ID, type = CommandType.UUID, entityType = IPAddressResponse.class, - description = "the ID of IP address of the firewall services") + description = "The ID of IP address of the firewall services") private Long ipAddressId; @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, - description = "list firewall rules for certain network", + description = "List firewall rules for certain network", since = "4.3") private Long networkId; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/ListPortForwardingRulesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/ListPortForwardingRulesCmd.java index a2e9152a9e47..e5fd35f61eb4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/ListPortForwardingRulesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/ListPortForwardingRulesCmd.java @@ -48,17 +48,17 @@ public class ListPortForwardingRulesCmd extends BaseListTaggedResourcesCmd { @Parameter(name = ApiConstants.IP_ADDRESS_ID, type = CommandType.UUID, entityType = IPAddressResponse.class, - description = "the ID of IP address of the port forwarding services") + description = "The ID of IP address of the port forwarding services") private Long ipAddressId; @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, - description = "list port forwarding rules for certain network", + description = "List port forwarding rules for certain network", since = "4.3") private Long networkId; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/UpdateEgressFirewallRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/UpdateEgressFirewallRuleCmd.java index a8db4ec2b29e..26d561dbe03d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/UpdateEgressFirewallRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/UpdateEgressFirewallRuleCmd.java @@ -42,14 +42,14 @@ public class UpdateEgressFirewallRuleCmd extends BaseAsyncCustomIdCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "the ID of the egress firewall rule") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "The ID of the egress firewall rule") private Long id; // unexposed parameter needed for events logging @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, expose = false) private Long ownerId; - @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}) + @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}) private Boolean display; // /////////////////////////////////////////////////// @@ -69,7 +69,7 @@ public Boolean getDisplay() { @Override public void execute() throws ResourceUnavailableException { - CallContext.current().setEventDetails("Rule Id: " + id); + CallContext.current().setEventDetails("Rule ID: " + getResourceUuid(ApiConstants.ID)); FirewallRule rule = _firewallService.updateEgressFirewallRule(id, this.getCustomId(), getDisplay()); FirewallResponse fwResponse = new FirewallResponse(); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/UpdateFirewallRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/UpdateFirewallRuleCmd.java index 89c9bc891eb0..1c2ea2b1897e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/UpdateFirewallRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/UpdateFirewallRuleCmd.java @@ -42,14 +42,14 @@ public class UpdateFirewallRuleCmd extends BaseAsyncCustomIdCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "the ID of the firewall rule") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "The ID of the firewall rule") private Long id; // unexposed parameter needed for events logging @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, expose = false) private Long ownerId; - @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}) + @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}) private Boolean display; // /////////////////////////////////////////////////// @@ -70,7 +70,7 @@ public Boolean getDisplay() { @Override public void execute() throws ResourceUnavailableException { - CallContext.current().setEventDetails("Rule ID: " + id); + CallContext.current().setEventDetails("Rule ID: " + getResourceUuid(ApiConstants.ID)); FirewallRule rule = _firewallService.updateIngressFirewallRule(id, this.getCustomId(), getDisplay()); FirewallResponse fwResponse = new FirewallResponse(); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/UpdatePortForwardingRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/UpdatePortForwardingRuleCmd.java index ee4bd18ad40a..aaeca27375b7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/UpdatePortForwardingRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/UpdatePortForwardingRuleCmd.java @@ -36,33 +36,33 @@ @APICommand(name = "updatePortForwardingRule", responseObject = FirewallRuleResponse.class, - description = "Updates a port forwarding rule. Only the private port and the virtual machine can be updated.", entityType = {PortForwardingRule.class}, + description = "Updates a port forwarding rule. Only the private port and the Instance can be updated.", entityType = {PortForwardingRule.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class UpdatePortForwardingRuleCmd extends BaseAsyncCustomIdCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "the ID of the port forwarding rule", since = "4.4") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "The ID of the port forwarding rule", since = "4.4") private Long id; - @Parameter(name=ApiConstants.PRIVATE_START_PORT, type=CommandType.INTEGER, description="the private start port of the port forwarding rule") + @Parameter(name=ApiConstants.PRIVATE_START_PORT, type=CommandType.INTEGER, description = "The private start port of the port forwarding rule") private Integer privatePort; - @Parameter(name=ApiConstants.PRIVATE_END_PORT, type=CommandType.INTEGER, description="the private end port of the port forwarding rule") + @Parameter(name=ApiConstants.PRIVATE_END_PORT, type=CommandType.INTEGER, description = "The private end port of the port forwarding rule") private Integer privateEndPort; @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, - description = "the ID of the virtual machine for the port forwarding rule") + description = "The ID of the Instance for the port forwarding rule") private Long virtualMachineId; - @Parameter(name = ApiConstants.VM_GUEST_IP, type = CommandType.STRING, required = false, description = "VM guest nic Secondary ip address for the port forwarding rule", since = "4.5") + @Parameter(name = ApiConstants.VM_GUEST_IP, type = CommandType.STRING, required = false, description = "Instance guest NIC Secondary IP address for the port forwarding rule", since = "4.5") private String vmGuestIp; - @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}) + @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}) private Boolean display; @Parameter(name = ApiConstants.CIDR_LIST, 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 413798970bf1..61e1d7aa5192 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 @@ -41,7 +41,7 @@ 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") diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCmd.java index b31a46692201..d558e4c4f245 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCmd.java @@ -42,13 +42,18 @@ public class ListGuestOsCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSResponse.class, description = "list by Os type Id") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSResponse.class, description = "List by OS type ID") private Long id; - @Parameter(name = ApiConstants.OS_CATEGORY_ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, description = "list by Os Category id") + @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, + entityType = GuestOSResponse.class, since = "4.22.1", + description = "Comma separated list of OS types") + private List ids; + + @Parameter(name = ApiConstants.OS_CATEGORY_ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, description = "List by OS Category ID") private Long osCategoryId; - @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "list os by description", since = "3.0.1") + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "List OS by description", since = "3.0.1") private String description; @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", @@ -63,6 +68,10 @@ public Long getId() { return id; } + public List getIds() { + return ids; + } + public Long getOsCategoryId() { return osCategoryId; } 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 index 8566b413cc12..2e3849cc8503 100644 --- 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 @@ -57,6 +57,10 @@ public class CreateGuiThemeCmd extends BaseCmd { "wildcard) separated by comma that can retrieve the theme; e.g.: *acme.com,acme2.com") private String commonNames; + @Parameter(name = ApiConstants.LOGIN_BASE_DOMAIN, type = CommandType.STRING, length = 65535, description = "The ACS domain to be used as base " + + "for the login when accessing the GUI through the common name defined in the theme. If a common name is not defined, this parameter is ignored on the GUI.") + private String loginBaseDomain; + @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; @@ -93,6 +97,10 @@ public String getCommonNames() { return commonNames; } + public String getLoginBaseDomain() { + return loginBaseDomain; + } + public String getDomainIds() { return domainIds; } 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 index daef2235ce89..e50961aa30d1 100644 --- 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 @@ -60,6 +60,10 @@ public class UpdateGuiThemeCmd extends BaseCmd { "wildcard) separated by comma that can retrieve the theme; e.g.: *acme.com,acme2.com") private String commonNames; + @Parameter(name = ApiConstants.LOGIN_BASE_DOMAIN, type = CommandType.STRING, length = 65535, description = "The ACS domain to be used as base for " + + "the login when accessing the GUI through the common name defined in the theme. If a common name is not defined, this parameter is ignored on the GUI.") + private String loginBaseDomain; + @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; @@ -96,6 +100,10 @@ public String getJsonConfiguration() { return jsonConfiguration; } + public String getLoginBaseDomain() { + return loginBaseDomain; + } + public String getCommonNames() { return commonNames; } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/CreateIpv6FirewallRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/CreateIpv6FirewallRuleCmd.java index 18af5b2973ee..237af7e4601b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/CreateIpv6FirewallRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/CreateIpv6FirewallRuleCmd.java @@ -43,7 +43,7 @@ import com.cloud.utils.net.NetUtils; @APICommand(name = "createIpv6FirewallRule", - description = "Creates an Ipv6 firewall rule in the given network (the network must not belong to VPC)", + description = "Creates an IPv6 firewall rule in the given Network (the Network must not belong to VPC)", responseObject = FirewallRuleResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, @@ -55,34 +55,34 @@ public class CreateIpv6FirewallRuleCmd extends BaseAsyncCreateCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, required = true, description = "the protocol for the Ipv6 firewall rule. Valid values are TCP/UDP/ICMP/ALL or valid protocol number") + @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, required = true, description = "The protocol for the Ipv6 firewall rule. Valid values are TCP/UDP/ICMP/ALL or valid protocol number") private String protocol; - @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "the starting port of Ipv6 firewall rule") + @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "The starting port of Ipv6 firewall rule") private Integer publicStartPort; - @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "the ending port of Ipv6 firewall rule") + @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "The ending port of Ipv6 firewall rule") private Integer publicEndPort; - @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the source CIDR list to allow traffic from. Multiple entries must be separated by a single comma character (,).") + @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "The source CIDR list to allow traffic from. Multiple entries must be separated by a single comma character (,).") private List sourceCidrList; - @Parameter(name = ApiConstants.DEST_CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the destination CIDR list to allow traffic to. Multiple entries must be separated by a single comma character (,).") + @Parameter(name = ApiConstants.DEST_CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "The destination CIDR list to allow traffic to. Multiple entries must be separated by a single comma character (,).") private List destinationCidrlist; - @Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "type of the ICMP message being sent") + @Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "Type of the ICMP message being sent") private Integer icmpType; - @Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "error code for this ICMP message") + @Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "Error code for this ICMP message") private Integer icmpCode; - @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "The network of the VM the Ipv6 firewall rule will be created for", required = true) + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "The Network of the Instance the Ipv6 firewall rule will be created for", required = true) private Long networkId; - @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, description = "the traffic type for the Ipv6 firewall rule, can be ingress or egress, defaulted to ingress if not specified") + @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, description = "The traffic type for the Ipv6 firewall rule, can be ingress or egress, defaulted to ingress if not specified") private String trafficType; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the rule to the end user or not", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "An optional field, whether to the display the rule to the end User or not", authorized = {RoleType.Admin}) private Boolean display; // /////////////////////////////////////////////////// @@ -193,7 +193,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Creating ipv6 firewall rule"; + return "Creating IPv6 firewall rule"; } public Integer getIcmpCode() { @@ -232,7 +232,7 @@ public void execute() throws ResourceUnavailableException { boolean success = false; FirewallRule rule = ipv6Service.getIpv6FirewallRule(getEntityId()); try { - CallContext.current().setEventDetails("Rule ID: " + getEntityId()); + CallContext.current().setEventDetails("Rule ID: " + getEntityUuid()); success = ipv6Service.applyIpv6FirewallRule(rule.getId()); // State is different after the rule is applied, so get new object here @@ -246,7 +246,7 @@ public void execute() throws ResourceUnavailableException { } finally { if (!success || rule == null) { ipv6Service.revokeIpv6FirewallRule(getEntityId()); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create ipv6 firewall rule"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create IPv6 firewall rule"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/DeleteIpv6FirewallRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/DeleteIpv6FirewallRuleCmd.java index aaee19b59489..6df5ce1438a1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/DeleteIpv6FirewallRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/DeleteIpv6FirewallRuleCmd.java @@ -45,7 +45,7 @@ public class DeleteIpv6FirewallRuleCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "the ID of the IPv6 firewall rule") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "The ID of the IPv6 firewall rule") private Long id; ///////////////////////////////////////////////////// @@ -66,7 +66,7 @@ public String getEventType() { @Override public String getEventDescription() { - return ("Deleting IPv6 firewall rule ID=" + id); + return "Deleting IPv6 firewall rule with ID:" + getResourceUuid(ApiConstants.ID); } @Override @@ -81,7 +81,7 @@ public long getEntityOwnerId() { @Override public void execute() throws ResourceUnavailableException { - CallContext.current().setEventDetails("IPv6 firewall rule ID: " + id); + CallContext.current().setEventDetails("IPv6 firewall rule ID: " + getResourceUuid(ApiConstants.ID)); boolean result = ipv6Service.revokeIpv6FirewallRule(id); if (result) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/ListIpv6FirewallRulesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/ListIpv6FirewallRulesCmd.java index 7ade2e3ed040..59401578cb25 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/ListIpv6FirewallRulesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/ListIpv6FirewallRulesCmd.java @@ -46,16 +46,16 @@ public class ListIpv6FirewallRulesCmd extends BaseListTaggedResourcesCmd impleme //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, - description = "Lists ipv6 firewall rule with the specified ID") + description = "Lists IPv6 firewall rule with the specified ID") private Long id; - @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "list ipv6 firewall rules by network ID") + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "List IPV6 firewall rules by Network ID") private Long networkId; - @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, description = "list ipv6 firewall rules by traffic type - ingress or egress") + @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, description = "List IPV6 firewall rules by traffic type - ingress or egress") private String trafficType; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", authorized = {RoleType.Admin}) private Boolean display; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/UpdateIpv6FirewallRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/UpdateIpv6FirewallRuleCmd.java index 2d63d703dc5e..f090de4e8849 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/UpdateIpv6FirewallRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/UpdateIpv6FirewallRuleCmd.java @@ -46,31 +46,31 @@ public class UpdateIpv6FirewallRuleCmd extends BaseAsyncCustomIdCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "the ID of the ipv6 firewall rule") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "The ID of the IPv6 firewall rule") private Long id; - @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "the protocol for the Ipv6 firewall rule. Valid values are TCP/UDP/ICMP/ALL or valid protocol number") + @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "The protocol for the Ipv6 firewall rule. Valid values are TCP/UDP/ICMP/ALL or valid protocol number") private String protocol; - @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "the starting port of Ipv6 firewall rule") + @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "The starting port of Ipv6 firewall rule") private Integer publicStartPort; - @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "the ending port of Ipv6 firewall rule") + @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "The ending port of Ipv6 firewall rule") private Integer publicEndPort; - @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the cidr list to allow traffic from/to. Multiple entries must be separated by a single comma character (,).") + @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "The cidr list to allow traffic from/to. Multiple entries must be separated by a single comma character (,).") private List cidrlist; - @Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "type of the ICMP message being sent") + @Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "Type of the ICMP message being sent") private Integer icmpType; - @Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "error code for this ICMP message") + @Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "Error code for this ICMP message") private Integer icmpCode; - @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, description = "the traffic type for the Ipv6 firewall rule, can be Ingress or Egress, defaulted to Ingress if not specified") + @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, description = "The traffic type for the Ipv6 firewall rule, can be Ingress or Egress, defaulted to Ingress if not specified") private String trafficType; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the Ipv6 firewall rule to the end user or not", since = "4.4", authorized = { + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "An optional field, whether to the display the Ipv6 firewall rule to the end User or not", since = "4.4", authorized = { RoleType.Admin}) private Boolean display; @@ -143,7 +143,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Updating ipv6 firewall rule"; + return "Updating IPv6 firewall rule"; } public Integer getIcmpCode() { @@ -156,7 +156,7 @@ public Integer getIcmpType() { @Override public void execute() throws ResourceUnavailableException { - CallContext.current().setEventDetails("Rule Id: " + getId()); + CallContext.current().setEventDetails("Rule ID: " + getResourceUuid(ApiConstants.ID)); FirewallRule rules = ipv6Service.updateIpv6FirewallRule(this); FirewallResponse ruleResponse = _responseGenerator.createIpv6FirewallRuleResponse(rules); setResponseObject(ruleResponse); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/AttachIsoCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/AttachIsoCmd.java index d795fbabb528..47d8d6c35f26 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/AttachIsoCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/AttachIsoCmd.java @@ -35,7 +35,7 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.uservm.UserVm; -@APICommand(name = "attachIso", description = "Attaches an ISO to a virtual machine.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, +@APICommand(name = "attachIso", description = "Attaches an ISO to an Instance.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class AttachIsoCmd extends BaseAsyncCmd implements UserCmd { @@ -46,11 +46,11 @@ public class AttachIsoCmd extends BaseAsyncCmd implements UserCmd { ///////////////////////////////////////////////////// @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TemplateResponse.class, - required = true, description = "the ID of the ISO file") + required = true, description = "The ID of the ISO file") protected Long id; @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, - required = true, description = "the ID of the virtual machine") + required = true, description = "The ID of the Instance") protected Long virtualMachineId; @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, @@ -86,7 +86,7 @@ public String getCommandName() { public long getEntityOwnerId() { UserVm vm = _entityMgr.findById(UserVm.class, getVirtualMachineId()); if (vm == null) { - throw new InvalidParameterValueException("Unable to find virtual machine by ID " + getVirtualMachineId()); + throw new InvalidParameterValueException("Unable to find Instance by ID " + getVirtualMachineId()); } return vm.getAccountId(); @@ -99,7 +99,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "attaching ISO: " + getId() + " to VM: " + getVirtualMachineId(); + return "Attaching ISO with ID: " + getResourceUuid(ApiConstants.ID) + " to Instance with ID: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID); } @Override @@ -114,7 +114,7 @@ public ApiCommandResourceType getApiResourceType() { @Override public void execute() { - CallContext.current().setEventDetails("Vm Id: " + getVirtualMachineId() + " ISO ID: " + getId()); + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID) + " ISO ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _templateService.attachIso(id, virtualMachineId, isForced()); if (result) { UserVm userVm = _responseGenerator.findUserVmById(virtualMachineId); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/DeleteIsoCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/DeleteIsoCmd.java index feae31026b9f..28dfd25b2428 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/DeleteIsoCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/DeleteIsoCmd.java @@ -41,13 +41,13 @@ public class DeleteIsoCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TemplateResponse.class, required = true, description = "the ID of the ISO file") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TemplateResponse.class, required = true, description = "The ID of the ISO file") private Long id; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, - description = "the ID of the zone of the ISO file. If not specified, the ISO will be deleted from all the zones") + description = "The ID of the zone of the ISO file. If not specified, the ISO will be deleted from all the zones") private Long zoneId; ///////////////////////////////////////////////////// @@ -87,7 +87,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Deleting ISO " + getId(); + return "Deleting ISO with ID: " + getResourceUuid(ApiConstants.ID) + " from zone " + getResourceUuid(ApiConstants.ZONE_ID); } @Override @@ -102,7 +102,7 @@ public Long getApiResourceId() { @Override public void execute() { - CallContext.current().setEventDetails("ISO Id: " + getId()); + CallContext.current().setEventDetails("ISO ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _templateService.deleteIso(this); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); 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 78f1a4bcdee6..2560d837de12 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 @@ -27,13 +27,14 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.command.user.UserCmd; import org.apache.cloudstack.api.command.user.vm.DeployVMCmd; +import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.UserVmResponse; import com.cloud.event.EventTypes; import com.cloud.exception.InvalidParameterValueException; import com.cloud.uservm.UserVm; -@APICommand(name = "detachIso", description = "Detaches any ISO file (if any) currently attached to a virtual machine.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, +@APICommand(name = "detachIso", description = "Detaches any ISO file (if any) currently attached to an Instance.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class DetachIsoCmd extends BaseAsyncCmd implements UserCmd { @@ -44,13 +45,17 @@ public class DetachIsoCmd extends BaseAsyncCmd implements UserCmd { ///////////////////////////////////////////////////// @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, - required = true, description = "The ID of the virtual machine") + required = true, description = "The ID of the Instance") protected Long virtualMachineId; @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, description = "If true, ejects the ISO before detaching on VMware. Default: false", since = "4.15.1") protected Boolean forced; + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TemplateResponse.class, + description = "The ID of the ISO to detach. Required when the Instance has more than one ISO attached.", since = "4.23.0") + protected Long id; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -78,7 +83,7 @@ public long getEntityOwnerId() { if (vm != null) { return vm.getAccountId(); } else { - throw new InvalidParameterValueException("Unable to find VM by ID " + getVirtualMachineId()); + throw new InvalidParameterValueException("Unable to find Instance by ID " + getVirtualMachineId()); } } @@ -89,7 +94,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "detaching ISO from VM: " + getVirtualMachineId(); + return "Detaching ISO from Instance with ID: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID); } @Override @@ -104,7 +109,7 @@ public ApiCommandResourceType getApiResourceType() { @Override public void execute() { - boolean result = _templateService.detachIso(virtualMachineId, null, isForced()); + boolean result = _templateService.detachIso(virtualMachineId, id, 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 4d11f0dc3d21..279db0f3104e 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,7 +17,6 @@ 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; @@ -44,20 +43,20 @@ public class ExtractIsoCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TemplateResponse.class, required = true, description = "the ID of the ISO file") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TemplateResponse.class, required = true, description = "The ID of the ISO file") private Long id; - @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = false, length = 2048, description = "the URL to which the ISO would be extracted") + @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = false, length = 2048, description = "The URL to which the ISO would be extracted") private String url; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = false, - description = "the ID of the zone where the ISO is originally located") + description = "The ID of the zone where the ISO is originally located") private Long zoneId; - @Parameter(name = ApiConstants.MODE, type = CommandType.STRING, required = true, description = "the mode of extraction - HTTP_DOWNLOAD or FTP_UPLOAD") + @Parameter(name = ApiConstants.MODE, type = CommandType.STRING, required = true, description = "The mode of extraction - HTTP_DOWNLOAD or FTP_UPLOAD") private String mode; ///////////////////////////////////////////////////// @@ -102,15 +101,13 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - String isoId = this._uuidMgr.getUuid(VirtualMachineTemplate.class, getId()); - String baseDescription = String.format("Extracting ISO: %s", isoId); + String description = "Extracting ISO: " + getResourceUuid(ApiConstants.ID); - Long zoneId = getZoneId(); - if (zoneId == null) { - return baseDescription; + if (getZoneId() == null) { + description += "from zone: " + getResourceUuid(ApiConstants.ZONE_ID); } - return String.format("%s from zone: %s", baseDescription, this._uuidMgr.getUuid(DataCenter.class, zoneId)); + return description; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/GetUploadParamsForIsoCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/GetUploadParamsForIsoCmd.java index 01a47f22b726..43cdf09a89cf 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/GetUploadParamsForIsoCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/GetUploadParamsForIsoCmd.java @@ -38,7 +38,7 @@ import com.cloud.exception.ResourceUnavailableException; @APICommand(name = "getUploadParamsForIso", - description = "upload an existing ISO into the CloudStack cloud.", + description = "Upload an existing ISO into the CloudStack cloud.", responseObject = GetUploadParamsResponse.class, since = "4.13", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -50,30 +50,30 @@ public class GetUploadParamsForIsoCmd extends AbstractGetUploadParamsCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.BOOTABLE, type = BaseCmd.CommandType.BOOLEAN, description = "true if this ISO is bootable. If not passed explicitly its assumed to be true") + @Parameter(name = ApiConstants.BOOTABLE, type = BaseCmd.CommandType.BOOLEAN, description = "True if this ISO is bootable. If not passed explicitly its assumed to be true") private Boolean bootable; @Parameter(name = ApiConstants.DISPLAY_TEXT, type = BaseCmd.CommandType.STRING, - description = "the display text of the ISO. This is usually used for display purposes.", + description = "The display text of the ISO. This is usually used for display purposes.", length = 4096) private String displayText; - @Parameter(name = ApiConstants.IS_FEATURED, type = BaseCmd.CommandType.BOOLEAN, description = "true if you want this ISO to be featured") + @Parameter(name = ApiConstants.IS_FEATURED, type = BaseCmd.CommandType.BOOLEAN, description = "True if you want this ISO to be featured") private Boolean featured; @Parameter(name = ApiConstants.IS_PUBLIC, type = BaseCmd.CommandType.BOOLEAN, - description = "true if you want to register the ISO to be publicly available to all users, false otherwise.") + description = "True if you want to register the ISO to be publicly available to all Users, false otherwise.") private Boolean publicIso; - @Parameter(name = ApiConstants.IS_EXTRACTABLE, type = BaseCmd.CommandType.BOOLEAN, description = "true if the ISO or its derivatives are extractable; default is false") + @Parameter(name = ApiConstants.IS_EXTRACTABLE, type = BaseCmd.CommandType.BOOLEAN, description = "True if the ISO or its derivatives are extractable; default is false") private Boolean extractable; @Parameter(name = ApiConstants.OS_TYPE_ID, type = BaseCmd.CommandType.UUID, entityType = GuestOSResponse.class, - description = "the ID of the OS type that best represents the OS of this ISO. If the ISO is bootable this parameter needs to be passed") + description = "The ID of the OS type that best represents the OS of this ISO. If the ISO is bootable this parameter needs to be passed") private Long osTypeId; ///////////////////////////////////////////////////// @@ -104,6 +104,29 @@ public Long getOsTypeId() { return osTypeId; } + public void setBootable(Boolean bootable) { + this.bootable = bootable; + } + + public void setDisplayText(String displayText) { + this.displayText = displayText; + } + + public void setFeatured(Boolean featured) { + this.featured = featured; + } + + public void setPublicIso(Boolean publicIso) { + this.publicIso = publicIso; + } + + public void setExtractable(Boolean extractable) { + this.extractable = extractable; + } + + public void setOsTypeId(Long osTypeId) { + this.osTypeId = osTypeId; + } ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// @@ -135,7 +158,7 @@ public String getCommandName() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(getAccountName(), getDomainId(), getProjectId(), true); + Long accountId = _accountService.finalizeAccountId(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/iso/ListIsoPermissionsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsoPermissionsCmd.java index 6f220c774b84..069f3e4959bb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsoPermissionsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsoPermissionsCmd.java @@ -1,4 +1,4 @@ -// Licensedname = "listIsoPermissions", to the Apache Software Foundation (ASF) under one +// 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 @@ -26,7 +26,7 @@ import com.cloud.storage.Storage.ImageFormat; import com.cloud.template.VirtualMachineTemplate; -@APICommand(name = "listIsoPermissions", description = "List ISO visibility and all accounts that have permissions to view this ISO.", responseObject = TemplatePermissionsResponse.class, responseView = ResponseView.Restricted, +@APICommand(name = "listIsoPermissions", description = "List ISO visibility and all Accounts that have permissions to view this ISO.", responseObject = TemplatePermissionsResponse.class, responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListIsoPermissionsCmd extends BaseListTemplateOrIsoPermissionsCmd implements UserCmd { 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 346eca8cff04..562cbc2c623d 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 @@ -45,49 +45,49 @@ public class ListIsosCmd extends BaseListTaggedResourcesCmd implements UserCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.BOOTABLE, type = CommandType.BOOLEAN, description = "true if the ISO is bootable, false otherwise") + @Parameter(name = ApiConstants.BOOTABLE, type = CommandType.BOOLEAN, description = "True if the ISO is bootable, false otherwise") private Boolean bootable; - @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "the hypervisor for which to restrict the search") + @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "The hypervisor for which to restrict the search") private String hypervisor; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "list ISO by ID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "List ISO by ID") private Long id; - @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "true if the ISO is publicly available to all users, false otherwise.") + @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 = "list ISOs that are ready to be deployed") + @Parameter(name = ApiConstants.IS_READY, type = CommandType.BOOLEAN, description = "If True, list ISOs that are ready to be deployed.") private Boolean ready; @Parameter(name = ApiConstants.ISO_FILTER, type = CommandType.STRING, - description = "possible values are \"featured\", \"self\", \"selfexecutable\",\"sharedexecutable\",\"executable\", and \"community\". " - + "* featured : templates that have been marked as featured and public. " - + "* self : templates that have been registered or created by the calling user. " - + "* selfexecutable : same as self, but only returns templates that can be used to deploy a new VM. " - + "* sharedexecutable : templates ready to be deployed that have been granted to the calling user by another user. " - + "* executable : templates that are owned by the calling user, or public templates, that can be used to deploy a VM. " - + "* community : templates that have been marked as public but not featured. " + "* all : all templates (only usable by admins).") + description = "Possible values are \"featured\", \"self\", \"selfexecutable\",\"sharedexecutable\",\"executable\", and \"community\". " + + "* featured : Templates that have been marked as featured and public. " + + "* self : Templates that have been registered or created by the calling User. " + + "* selfexecutable : same as self, but only returns Templates that can be used to deploy a new Instance. " + + "* sharedexecutable : Templates ready to be deployed that have been granted to the calling User by another User. " + + "* executable : Templates that are owned by the calling User, or public Templates, that can be used to deploy an Instance. " + + "* community : Templates that have been marked as public but not featured. " + "* all : all Templates (only usable by admins).") private String isoFilter = TemplateFilter.selfexecutable.toString(); - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "list all ISOs by name") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "List all ISOs by name") private String isoName; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the ID of the zone") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The ID of the zone") private Long zoneId; - @Parameter(name=ApiConstants.SHOW_REMOVED, type=CommandType.BOOLEAN, description="show removed ISOs as well") + @Parameter(name=ApiConstants.SHOW_REMOVED, type=CommandType.BOOLEAN, description = "Show removed ISOs as well") private Boolean showRemoved; - @Parameter(name = ApiConstants.SHOW_UNIQUE, type = CommandType.BOOLEAN, description = "If set to true, list only unique isos across zones", since = "4.13.2") + @Parameter(name = ApiConstants.SHOW_UNIQUE, type = CommandType.BOOLEAN, description = "If set to true, list only unique ISOs across zones", since = "4.13.2") private Boolean showUnique; - @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, description = "flag to display the resource image for the isos") + @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, description = "Flag to display the resource image for the ISOs") private Boolean showIcon; @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, - description = "the CPU arch of the ISO. Valid options are: x86_64, aarch64", + description = "the CPU arch of the ISO. Valid options are: x86_64, aarch64, s390x", since = "4.20") private String arch; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java index 81f525522895..1c57e902e221 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java @@ -50,52 +50,52 @@ public class RegisterIsoCmd extends BaseCmd implements UserCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.BOOTABLE, type = CommandType.BOOLEAN, description = "true if this ISO is bootable. If not passed explicitly its assumed to be true") + @Parameter(name = ApiConstants.BOOTABLE, type = CommandType.BOOLEAN, description = "True if this ISO is bootable. If not passed explicitly its assumed to be true") private Boolean bootable; @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, - description = "the display text of the ISO, defaults to the 'name'", + description = "The display text of the ISO, defaults to the 'name'", length = 4096) private String displayText; - @Parameter(name = ApiConstants.IS_FEATURED, type = CommandType.BOOLEAN, description = "true if you want this ISO to be featured") + @Parameter(name = ApiConstants.IS_FEATURED, type = CommandType.BOOLEAN, description = "True if you want this ISO to be featured") private Boolean featured; @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, - description = "true if you want to register the ISO to be publicly available to all users, false otherwise.") + description = "True if you want to register the ISO to be publicly available to all Users, false otherwise.") private Boolean publicIso; - @Parameter(name = ApiConstants.IS_EXTRACTABLE, type = CommandType.BOOLEAN, description = "true if the ISO or its derivatives are extractable; default is false") + @Parameter(name = ApiConstants.IS_EXTRACTABLE, type = CommandType.BOOLEAN, description = "True if the ISO or its derivatives are extractable; default is false") private Boolean extractable; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the ISO") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, length = 251, description = "The name of the ISO") private String isoName; @Parameter(name = ApiConstants.OS_TYPE_ID, type = CommandType.UUID, entityType = GuestOSResponse.class, - description = "the ID of the OS type that best represents the OS of this ISO. If the ISO is bootable this parameter needs to be passed") + description = "The ID of the OS type that best represents the OS of this ISO. If the ISO is bootable this parameter needs to be passed") private Long osTypeId; - @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, length = 2048, description = "the URL to where the ISO is currently being hosted") + @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, length = 2048, description = "The URL to where the ISO is currently being hosted") private String url; @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.UUID, entityType = ZoneResponse.class, - required=true, description="the ID of the zone you wish to register the ISO to.") + required=true, description = "The ID of the zone you wish to register the ISO to.") protected Long zoneId; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "an optional domainId. If the account parameter is used, domainId must also be used.") + description = "An optional domainId. If the Account parameter is used, domainId must also be used.") private Long domainId; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account name. Must be used with domainId.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "An optional Account name. Must be used with domainId.") private String accountName; - @Parameter(name = ApiConstants.CHECKSUM, type = CommandType.STRING, description = "the checksum value of this ISO. " + ApiConstants.CHECKSUM_PARAMETER_PREFIX_DESCRIPTION) + @Parameter(name = ApiConstants.CHECKSUM, type = CommandType.STRING, description = "The checksum value of this ISO. " + ApiConstants.CHECKSUM_PARAMETER_PREFIX_DESCRIPTION) private String checksum; @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Register ISO for the project") @@ -106,21 +106,21 @@ public class RegisterIsoCmd extends BaseCmd implements UserCmd { @Parameter(name = ApiConstants.IS_DYNAMICALLY_SCALABLE, type = CommandType.BOOLEAN, - description = "true if ISO contains XS/VMWare tools inorder to support dynamic scaling of VM CPU/memory") + description = "True if ISO contains XS/VMWare tools in order to support dynamic scaling of Instance CPU/memory") protected Boolean isDynamicallyScalable; @Parameter(name=ApiConstants.DIRECT_DOWNLOAD, type = CommandType.BOOLEAN, - description = "true if ISO should bypass Secondary Storage and be downloaded to Primary Storage on deployment") + description = "True if ISO should bypass Secondary Storage and be downloaded to Primary Storage on deployment") private Boolean directDownload; @Parameter(name = ApiConstants.PASSWORD_ENABLED, type = CommandType.BOOLEAN, - description = "true if password reset feature is supported; default is false") + description = "True if password reset feature is supported; default is false") private Boolean passwordEnabled; @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, - description = "the CPU arch of the ISO. Valid options are: x86_64, aarch64", + description = "the CPU arch of the ISO. Valid options are: x86_64, aarch64, s390x", since = "4.20") private String arch; @@ -254,7 +254,7 @@ public String getCommandName() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/job/ListAsyncJobsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/job/ListAsyncJobsCmd.java index 783d78fdce32..2c8401831132 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/job/ListAsyncJobsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/job/ListAsyncJobsCmd.java @@ -19,6 +19,7 @@ import java.util.Date; import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiArgValidator; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseListAccountResourcesCmd; import org.apache.cloudstack.api.Parameter; @@ -26,7 +27,7 @@ import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.ManagementServerResponse; -@APICommand(name = "listAsyncJobs", description = "Lists all pending asynchronous jobs for the account.", responseObject = AsyncJobResponse.class, +@APICommand(name = "listAsyncJobs", description = "Lists all pending asynchronous jobs for the Account.", responseObject = AsyncJobResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListAsyncJobsCmd extends BaseListAccountResourcesCmd { @@ -40,6 +41,12 @@ public class ListAsyncJobsCmd extends BaseListAccountResourcesCmd { @Parameter(name = ApiConstants.MANAGEMENT_SERVER_ID, type = CommandType.UUID, entityType = ManagementServerResponse.class, description = "The id of the management server", since="4.19") private Long managementServerId; + @Parameter(name = ApiConstants.RESOURCE_ID, validations = {ApiArgValidator.UuidString}, type = CommandType.STRING, description = "the ID of the resource associated with the job", since="4.22.1") + private String resourceId; + + @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, description = "the type of the resource associated with the job", since="4.22.1") + private String resourceType; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -52,6 +59,14 @@ public Long getManagementServerId() { return managementServerId; } + public String getResourceId() { + return resourceId; + } + + public String getResourceType() { + return resourceType; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/job/QueryAsyncJobResultCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/job/QueryAsyncJobResultCmd.java index 3d328543dc29..5c3b0084574b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/job/QueryAsyncJobResultCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/job/QueryAsyncJobResultCmd.java @@ -16,8 +16,8 @@ // under the License. package org.apache.cloudstack.api.command.user.job; - import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiArgValidator; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; @@ -34,9 +34,15 @@ public class QueryAsyncJobResultCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.JOB_ID, type = CommandType.UUID, entityType = AsyncJobResponse.class, required = true, description = "the ID of the asynchronous job") + @Parameter(name = ApiConstants.JOB_ID, type = CommandType.UUID, entityType = AsyncJobResponse.class, description = "The ID of the asynchronous job") private Long id; + @Parameter(name = ApiConstants.RESOURCE_ID, validations = {ApiArgValidator.UuidString}, type = CommandType.STRING, description = "the ID of the resource associated with the job", since="4.22.1") + private String resourceId; + + @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, description = "the type of the resource associated with the job", since="4.22.1") + private String resourceType; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -45,6 +51,14 @@ public Long getId() { return id; } + public String getResourceId() { + return resourceId; + } + + public String getResourceType() { + return resourceType; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/kms/CreateKMSKeyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/kms/CreateKMSKeyCmd.java new file mode 100644 index 000000000000..c2b8fb6fe09e --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/kms/CreateKMSKeyCmd.java @@ -0,0 +1,159 @@ +/* + * 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.kms; + +import com.cloud.exception.ResourceAllocationException; +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.command.user.UserCmd; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.HSMProfileResponse; +import org.apache.cloudstack.api.response.KMSKeyResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.framework.kms.KMSException; +import org.apache.cloudstack.kms.KMSManager; + +import javax.inject.Inject; + +@APICommand(name = "createKMSKey", + description = "Creates a new KMS key (Key Encryption Key) for encryption", + responseObject = KMSKeyResponse.class, + since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false) +public class CreateKMSKeyCmd extends BaseCmd implements UserCmd { + + @Inject + private KMSManager kmsManager; + + @Parameter(name = ApiConstants.NAME, + required = true, + type = CommandType.STRING, + description = "Name of the KMS key") + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, + type = CommandType.STRING, + description = "Description of the KMS key") + private String description; + + @Parameter(name = ApiConstants.ZONE_ID, + required = true, + type = CommandType.UUID, + entityType = ZoneResponse.class, + description = "Zone ID where the key will be valid") + private Long zoneId; + + @Parameter(name = ApiConstants.ACCOUNT, + type = CommandType.STRING, + description = "Account name (for creating keys for child accounts - requires domain admin or admin)") + private String accountName; + + @Parameter(name = ApiConstants.DOMAIN_ID, + type = CommandType.UUID, + entityType = DomainResponse.class, + description = "Domain ID (for creating keys for child accounts - requires domain admin or admin)") + private Long domainId; + + @Parameter(name = ApiConstants.PROJECT_ID, + type = CommandType.UUID, + entityType = ProjectResponse.class, + description = "ID of the project to create the KMS key for") + private Long projectId; + + @Parameter(name = ApiConstants.KEY_BITS, + type = CommandType.INTEGER, + description = "Key size in bits: 128, 192, or 256 (default: 256)") + private Integer keyBits; + + @Parameter(name = ApiConstants.HSM_PROFILE_ID, + type = CommandType.UUID, + entityType = HSMProfileResponse.class, + required = true, + description = "ID of HSM profile to create key in") + private Long hsmProfileId; + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public Long getZoneId() { + return zoneId; + } + + public String getAccountName() { + return accountName; + } + + public Long getDomainId() { + return domainId; + } + + public Long getProjectId() { + return projectId; + } + + public Integer getKeyBits() { + return keyBits != null ? keyBits : 256; + } + + public Long getHsmProfileId() { + return hsmProfileId; + } + + @Override + public void execute() throws ResourceAllocationException { + try { + KMSKeyResponse response = kmsManager.createKMSKey(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (KMSException e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + "Failed to create KMS key: " + e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); + if (accountId != null) { + return accountId; + } + return CallContext.current().getCallingAccount().getId(); + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.KmsKey; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/kms/DeleteKMSKeyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/kms/DeleteKMSKeyCmd.java new file mode 100644 index 000000000000..58f67ec60c4d --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/kms/DeleteKMSKeyCmd.java @@ -0,0 +1,104 @@ +/* + * 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.kms; + +import com.cloud.event.EventTypes; +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.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.KMSKeyResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.framework.kms.KMSException; +import org.apache.cloudstack.kms.KMSKey; +import org.apache.cloudstack.kms.KMSManager; + +import javax.inject.Inject; + +@APICommand(name = "deleteKMSKey", + description = "Deletes a KMS key (only if not in use)", + responseObject = SuccessResponse.class, + since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false) +public class DeleteKMSKeyCmd extends BaseAsyncCmd implements UserCmd { + + @Inject + private KMSManager kmsManager; + + @Parameter(name = ApiConstants.ID, + required = true, + type = CommandType.UUID, + entityType = KMSKeyResponse.class, + description = "The UUID of the KMS key to delete") + private Long id; + + @Override + public void execute() { + try { + SuccessResponse response = kmsManager.deleteKMSKey(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (KMSException e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + "Failed to delete KMS key: " + e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + KMSKey key = _entityMgr.findById(KMSKey.class, id); + if (key != null) { + return key.getAccountId(); + } + return CallContext.current().getCallingAccount().getId(); + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.KmsKey; + } + + @Override + public Long getApiResourceId() { + return getId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_KMS_KEY_DELETE; + } + + @Override + public String getEventDescription() { + return "deleting KMS key: " + getId(); + } + + public Long getId() { + return id; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/kms/ListKMSKeysCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/kms/ListKMSKeysCmd.java new file mode 100644 index 000000000000..c6e4292099fe --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/kms/ListKMSKeysCmd.java @@ -0,0 +1,101 @@ +/* + * 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.kms; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd; +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.HSMProfileResponse; +import org.apache.cloudstack.api.response.KMSKeyResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.kms.KMSManager; + +import javax.inject.Inject; + +@APICommand(name = "listKMSKeys", + description = "Lists KMS keys available to the caller", + responseObject = KMSKeyResponse.class, + responseView = ResponseView.Restricted, + since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false) +public class ListKMSKeysCmd extends BaseListProjectAndAccountResourcesCmd implements UserCmd { + private static final String s_name = "listkmskeysresponse"; + + @Inject + private KMSManager kmsManager; + + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + entityType = KMSKeyResponse.class, + description = "List KMS key by UUID") + private Long id; + + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.UUID, + entityType = ZoneResponse.class, + description = "Filter by zone ID") + private Long zoneId; + + @Parameter(name = ApiConstants.ENABLED, + type = CommandType.BOOLEAN, + description = "Filter by enabled status") + private Boolean enabled; + + @Parameter(name = ApiConstants.HSM_PROFILE_ID, + type = CommandType.UUID, + entityType = HSMProfileResponse.class, + description = "Filter by HSM profile ID") + private Long hsmProfileId; + + public Long getId() { + return id; + } + + public Long getZoneId() { + return zoneId; + } + + public Boolean getEnabled() { + return enabled; + } + + public Long getHsmProfileId() { + return hsmProfileId; + } + + @Override + public void execute() { + ListResponse listResponse = kmsManager.listKMSKeys(this); + listResponse.setResponseName(getCommandName()); + setResponseObject(listResponse); + } + + @Override + public String getCommandName() { + return s_name; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/kms/RotateKMSKeyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/kms/RotateKMSKeyCmd.java new file mode 100644 index 000000000000..e3c6c5822b2f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/kms/RotateKMSKeyCmd.java @@ -0,0 +1,128 @@ +// 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.kms; + +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.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.AsyncJobResponse; +import org.apache.cloudstack.api.response.HSMProfileResponse; +import org.apache.cloudstack.api.response.KMSKeyResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.framework.kms.KMSException; +import org.apache.cloudstack.kms.KMSKey; +import org.apache.cloudstack.kms.KMSManager; + +import javax.inject.Inject; + +@APICommand(name = "rotateKMSKey", + description = "Rotates KMS key (KEK) by creating new version and scheduling gradual re-encryption", + responseObject = AsyncJobResponse.class, + since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false) +public class RotateKMSKeyCmd extends BaseAsyncCmd { + private static final String s_name = "rotatekmskeyresponse"; + + @Inject + private KMSManager kmsManager; + + @Parameter(name = ApiConstants.ID, + required = true, + type = CommandType.UUID, + entityType = KMSKeyResponse.class, + description = "KMS Key UUID to rotate") + private Long id; + + @Parameter(name = ApiConstants.KEY_BITS, + type = CommandType.INTEGER, + description = "Key size for new KEK (default: same as current)") + private Integer keyBits; + + @Parameter(name = ApiConstants.HSM_PROFILE_ID, + type = CommandType.UUID, + entityType = HSMProfileResponse.class, + description = "The target HSM profile ID for the new KEK version. If provided, migrates the key to " + + "this HSM.") + private Long hsmProfileId; + + public Long getId() { + return id; + } + + public Integer getKeyBits() { + return keyBits; + } + + public Long getHsmProfileId() { + return hsmProfileId; + } + + @Override + public void execute() { + try { + kmsManager.rotateKMSKey(this); + SuccessResponse response = new SuccessResponse(); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (KMSException e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + "Failed to rotate KMS key: " + e.getMessage()); + } + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + KMSKey key = _entityMgr.findById(KMSKey.class, id); + if (key != null) { + return key.getAccountId(); + } + return CallContext.current().getCallingAccount().getId(); + } + + @Override + public String getEventType() { + return com.cloud.event.EventTypes.EVENT_KMS_KEY_ROTATE; + } + + @Override + public String getEventDescription() { + return "Rotating KMS key: " + _uuidMgr.getUuid(KMSKey.class, id); + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.KmsKey; + } + + @Override + public Long getApiResourceId() { + return getId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/kms/UpdateKMSKeyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/kms/UpdateKMSKeyCmd.java new file mode 100644 index 000000000000..77654aa13ac3 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/kms/UpdateKMSKeyCmd.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.user.kms; + +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.KMSKeyResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.framework.kms.KMSException; +import org.apache.cloudstack.kms.KMSManager; + +import javax.inject.Inject; + +@APICommand(name = "updateKMSKey", + description = "Updates KMS key name, description, or enabled status", + responseObject = KMSKeyResponse.class, + since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false) +public class UpdateKMSKeyCmd extends BaseCmd { + + @Inject + private KMSManager kmsManager; + + @Parameter(name = ApiConstants.ID, + required = true, + type = CommandType.UUID, + entityType = KMSKeyResponse.class, + description = "The UUID of the KMS key to update") + private Long id; + + @Parameter(name = ApiConstants.NAME, + type = CommandType.STRING, + description = "New name for the key") + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, + type = CommandType.STRING, + description = "New description for the key") + private String description; + + @Parameter(name = ApiConstants.ENABLED, + type = CommandType.BOOLEAN, + description = "whether the key should be enabled") + private Boolean enabled; + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public Boolean getEnabled() { + return enabled; + } + + @Override + public void execute() { + try { + KMSKeyResponse response = kmsManager.updateKMSKey(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (KMSException e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + "Failed to update KMS key: " + e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.KmsKey; + } + + @Override + public Long getApiResourceId() { + return getId(); + } + + public Long getId() { + return id; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/kms/hsm/CreateHSMProfileCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/kms/hsm/CreateHSMProfileCmd.java new file mode 100644 index 000000000000..496ddee89b27 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/kms/hsm/CreateHSMProfileCmd.java @@ -0,0 +1,158 @@ +// 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.kms.hsm; + +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.utils.StringUtils; +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.DomainResponse; +import org.apache.cloudstack.api.response.HSMProfileResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.framework.kms.KMSException; +import org.apache.cloudstack.kms.HSMProfile; +import org.apache.cloudstack.kms.KMSManager; + +import javax.inject.Inject; +import java.util.Map; + +@APICommand(name = "createHSMProfile", description = "Creates a new HSM profile", responseObject = HSMProfileResponse.class, + requestHasSensitiveInfo = true, responseHasSensitiveInfo = true, since = "4.23.0", + authorized = { RoleType.Admin }) +public class CreateHSMProfileCmd extends BaseCmd { + + @Inject + private KMSManager kmsManager; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, + description = "the name of the HSM profile") + private String name; + + @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, + description = "the protocol of the HSM profile (PKCS11, KMIP, etc.). Default is 'pkcs11'") + private String protocol; + + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, + description = "the zone ID where the HSM profile is available. If null, global scope (for admin only)") + private Long zoneId; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, + description = "the domain ID where the HSM profile is available. If specified without an account, " + + "the profile is domain-scoped and usable by all accounts directly in that domain.") + private Long domainId; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, + description = "the account name of the HSM profile owner. Must be used with domainId. " + + "Leave empty (with a domainId) to create a domain-scoped profile shared by all " + + "accounts in that domain.") + private String accountName; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, + description = "the ID of the project to add the HSM profile for") + private Long projectId; + + @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, + description = "whether this is a public HSM profile available to all users globally (root admin only). " + + "Default is false") + private Boolean isPublic; + + @Parameter(name = ApiConstants.VENDOR_NAME, type = CommandType.STRING, description = "the vendor name of the HSM") + private String vendorName; + + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, + description = "HSM configuration details (protocol specific)") + private Map details; + + public String getName() { + return name; + } + + public String getProtocol() { + if (StringUtils.isBlank(protocol)) { + return "pkcs11"; + } + return protocol; + } + + public Long getZoneId() { + return zoneId; + } + + public Long getDomainId() { + return domainId; + } + + public String getAccountName() { + return accountName; + } + + public Long getProjectId() { + return projectId; + } + + public Boolean getIsPublic() { + return isPublic != null && isPublic; + } + + public String getVendorName() { + return vendorName; + } + + public Map getDetails() { + return convertDetailsToMap(details); + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, + ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + HSMProfile profile = kmsManager.addHSMProfile(this); + HSMProfileResponse response = kmsManager.createHSMProfileResponse(profile); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (KMSException e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); + if (accountId != null) { + return accountId; + } + return CallContext.current().getCallingAccount().getId(); + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.HsmProfile; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/kms/hsm/DeleteHSMProfileCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/kms/hsm/DeleteHSMProfileCmd.java new file mode 100644 index 000000000000..575a4447a69a --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/kms/hsm/DeleteHSMProfileCmd.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.user.kms.hsm; + +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 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.HSMProfileResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.framework.kms.KMSException; +import org.apache.cloudstack.kms.HSMProfile; +import org.apache.cloudstack.kms.KMSManager; + +import javax.inject.Inject; + +@APICommand(name = "deleteHSMProfile", description = "Deletes an HSM profile", responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.23.0", + authorized = { RoleType.Admin }) +public class DeleteHSMProfileCmd extends BaseCmd { + + @Inject + private KMSManager kmsManager; + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HSMProfileResponse.class, required = true, + description = "the ID of the HSM profile") + private Long id; + + public Long getId() { + return id; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, + ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + boolean result = kmsManager.deleteHSMProfile(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete HSM profile"); + } + } catch (KMSException e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + HSMProfile profile = _entityMgr.findById(HSMProfile.class, id); + if (profile != null && profile.getAccountId() > 0) { + return profile.getAccountId(); + } + return CallContext.current().getCallingAccount().getId(); + } + + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.HsmProfile; + } + + @Override + public Long getApiResourceId() { + return getId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/kms/hsm/ListHSMProfilesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/kms/hsm/ListHSMProfilesCmd.java new file mode 100644 index 000000000000..dc5bb589c42c --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/kms/hsm/ListHSMProfilesCmd.java @@ -0,0 +1,85 @@ +// 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.kms.hsm; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.HSMProfileResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.kms.KMSManager; + +import javax.inject.Inject; + +@APICommand(name = "listHSMProfiles", description = "Lists HSM profiles", responseObject = HSMProfileResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = true, since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class ListHSMProfilesCmd extends BaseListProjectAndAccountResourcesCmd { + + @Inject + private KMSManager kmsManager; + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HSMProfileResponse.class, + description = "the HSM profile ID") + private Long id; + + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, + description = "the zone ID") + private Long zoneId; + + @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "the protocol of the HSM profile") + private String protocol; + + @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, description = "list only enabled profiles") + private Boolean enabled; + + @Parameter(name = ApiConstants.IS_PUBLIC, + type = CommandType.BOOLEAN, + description = "when true, non-admin users see only system (global) profiles") + private Boolean isPublic; + + public Long getId() { + return id; + } + + public Long getZoneId() { + return zoneId; + } + + public String getProtocol() { + return protocol; + } + + public Boolean getEnabled() { + return enabled; + } + + public Boolean getIsPublic() { + return isPublic; + } + + @Override + public void execute() { + ListResponse response = kmsManager.listHSMProfiles(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/kms/hsm/UpdateHSMProfileCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/kms/hsm/UpdateHSMProfileCmd.java new file mode 100644 index 000000000000..fea7807d616d --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/kms/hsm/UpdateHSMProfileCmd.java @@ -0,0 +1,104 @@ +// 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.kms.hsm; + +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 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.HSMProfileResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.framework.kms.KMSException; +import org.apache.cloudstack.kms.HSMProfile; +import org.apache.cloudstack.kms.KMSManager; + +import javax.inject.Inject; + +@APICommand(name = "updateHSMProfile", description = "Updates an HSM profile", + responseObject = HSMProfileResponse.class, + requestHasSensitiveInfo = true, responseHasSensitiveInfo = true, since = "4.23.0", + authorized = { RoleType.Admin }) +public class UpdateHSMProfileCmd extends BaseCmd { + + @Inject + private KMSManager kmsManager; + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HSMProfileResponse.class, required = true, + description = "the ID of the HSM profile") + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the HSM profile") + private String name; + + @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, + description = "whether the HSM profile is enabled") + private Boolean enabled; + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public Boolean getEnabled() { + return enabled; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, + ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + HSMProfile profile = kmsManager.updateHSMProfile(this); + HSMProfileResponse response = kmsManager.createHSMProfileResponse(profile); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (KMSException e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + HSMProfile profile = _entityMgr.findById(HSMProfile.class, id); + if (profile != null && profile.getAccountId() > 0) { + return profile.getAccountId(); + } + return CallContext.current().getCallingAccount().getId(); + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.HsmProfile; + } + + @Override + public Long getApiResourceId() { + return getId(); + } +} 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 bfc155468404..c9b31dc84271 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 @@ -48,14 +48,14 @@ public class AssignCertToLoadBalancerCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, - description = "the ID of the load balancer rule") + description = "The ID of the load balancer rule") Long lbRuleId; @Parameter(name = ApiConstants.CERTIFICATE_ID, type = CommandType.UUID, entityType = SslCertResponse.class, required = true, - description = "the ID of the certificate") + description = "The ID of the certificate") Long certId; @Parameter(name = ApiConstants.FORCED, @@ -83,7 +83,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Assigning a certificate to a load balancer"; + return "Assigning certificate with ID: " + getResourceUuid(ApiConstants.CERTIFICATE_ID) + " to load balancer with ID: " + getResourceUuid(ApiConstants.LBID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/AssignToLoadBalancerRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/AssignToLoadBalancerRuleCmd.java index c4424b1d9374..cc7cd2382b75 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/AssignToLoadBalancerRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/AssignToLoadBalancerRuleCmd.java @@ -23,6 +23,8 @@ import java.util.List; import java.util.Map; +import com.cloud.network.Network; +import com.cloud.utils.Pair; import com.cloud.utils.exception.CloudRuntimeException; import org.apache.cloudstack.api.APICommand; @@ -45,7 +47,7 @@ import com.cloud.vm.VirtualMachine; @APICommand(name = "assignToLoadBalancerRule", - description = "Assigns virtual machine or a list of virtual machines to a load balancer rule.", + description = "Assigns an Instance or a list of Instances to a load balancer rule.", responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -60,19 +62,19 @@ public class AssignToLoadBalancerRuleCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, - description = "the ID of the load balancer rule") + description = "The ID of the load balancer rule") private Long id; @Parameter(name = ApiConstants.VIRTUAL_MACHINE_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = UserVmResponse.class, - description = "the list of IDs of the virtual machine that are being assigned to the load balancer rule(i.e. virtualMachineIds=1,2,3)") + description = "The list of IDs of the Instance that are being assigned to the load balancer rule(i.e. virtualMachineIds=1,2,3)") private List virtualMachineIds; @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID_IP, type = CommandType.MAP, - description = "VM ID and IP map, vmidipmap[0].vmid=1 vmidipmap[0].vmip=10.1.1.75", + description = "VM ID and IP map, vmidipmap[0].vmid=1 vmidipmap[0].vmip=10.1.1.75. (Optional, for VPC Conserve Mode) Pass vmnetworkid. Example: vmidipmap[0].vmnetworkid=NETWORK_TIER_UUID", since = "4.4") private Map vmIdIpMap; @@ -112,12 +114,13 @@ public String getEventType() { @Override public String getEventDescription() { - return "applying instances for load balancer: " + getLoadBalancerId() + " (ids: " + StringUtils.join(getVirtualMachineIds(), ",") + ")"; + return "Applying Instances for load balancer with ID: " + getResourceUuid(ApiConstants.ID) + " (Instances IDs: " + StringUtils.join(getVirtualMachineIds(), ",") + ")"; } - public Map> getVmIdIpListMap() { - Map> vmIdIpsMap = new HashMap>(); + public Pair>, Map> getVmIdIpListMapAndVmIdNetworkMap() { + Map> vmIdIpsMap = new HashMap<>(); + Map vmIdNetworkMap = new HashMap<>(); if (vmIdIpMap != null && !vmIdIpMap.isEmpty()) { Collection idIpsCollection = vmIdIpMap.values(); Iterator iter = idIpsCollection.iterator(); @@ -125,10 +128,11 @@ public Map> getVmIdIpListMap() { HashMap idIpsMap = (HashMap)iter.next(); String vmId = idIpsMap.get("vmid"); String vmIp = idIpsMap.get("vmip"); + String vmNetworkUuid = idIpsMap.get("vmnetworkid"); VirtualMachine lbvm = _entityMgr.findByUuid(VirtualMachine.class, vmId); if (lbvm == null) { - throw new InvalidParameterValueException("Unable to find virtual machine ID: " + vmId); + throw new InvalidParameterValueException("Unable to find Instance ID: " + vmId); } //check whether the given ip is valid ip or not @@ -145,25 +149,35 @@ public Map> getVmIdIpListMap() { ipsList = new ArrayList(); } ipsList.add(vmIp); + + if (vmNetworkUuid != null) { + Network vmNetwork = _entityMgr.findByUuid(Network.class, vmNetworkUuid); + if (vmNetwork == null) { + throw new InvalidParameterValueException("Unable to find Network ID: " + vmNetworkUuid); + } + vmIdNetworkMap.put(longVmId, vmNetwork.getId()); + } vmIdIpsMap.put(longVmId, ipsList); } } - return vmIdIpsMap; + return new Pair<>(vmIdIpsMap, vmIdNetworkMap); } @Override public void execute() { - CallContext.current().setEventDetails("Load balancer Id: " + getLoadBalancerId() + " VmIds: " + StringUtils.join(getVirtualMachineIds(), ",")); + CallContext.current().setEventDetails("Load balancer ID: " + getResourceUuid(ApiConstants.ID) + " Instances IDs: " + StringUtils.join(getVirtualMachineIds(), ",")); - Map> vmIdIpsMap = getVmIdIpListMap(); + Pair>, Map> mapsPair = getVmIdIpListMapAndVmIdNetworkMap(); + Map> vmIdIpsMap = mapsPair.first(); + Map vmIdNetworkMap = mapsPair.second(); boolean result = false; try { - result = _lbService.assignToLoadBalancer(getLoadBalancerId(), virtualMachineIds, vmIdIpsMap, false); + result = _lbService.assignToLoadBalancer(getLoadBalancerId(), virtualMachineIds, vmIdIpsMap, vmIdNetworkMap, false); }catch (CloudRuntimeException ex) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to assign load balancer rule"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to assign load balancer rule due to: " + ex.getMessage()); } if (result) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateApplicationLoadBalancerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateApplicationLoadBalancerCmd.java index 2199dfb4e8b4..ae9eb31a2292 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateApplicationLoadBalancerCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateApplicationLoadBalancerCmd.java @@ -48,51 +48,51 @@ public class CreateApplicationLoadBalancerCmd extends BaseAsyncCreateCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "name of the load balancer") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the load balancer") private String loadBalancerName; - @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "the description of the load balancer", length = 4096) + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "The description of the load balancer", length = 4096) private String description; @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, required = true, entityType = NetworkResponse.class, - description = "The guest network the load balancer will be created for") + description = "The guest Network the load balancer will be created for") private Long networkId; @Parameter(name = ApiConstants.SOURCE_PORT, type = CommandType.INTEGER, required = true, - description = "the source port the network traffic will be load balanced from") + description = "The source port the Network traffic will be load balanced from") private Integer sourcePort; - @Parameter(name = ApiConstants.ALGORITHM, type = CommandType.STRING, required = true, description = "load balancer algorithm (source, roundrobin, leastconn)") + @Parameter(name = ApiConstants.ALGORITHM, type = CommandType.STRING, required = true, description = "Load balancer algorithm (source, roundrobin, leastconn)") private String algorithm; @Parameter(name = ApiConstants.INSTANCE_PORT, type = CommandType.INTEGER, required = true, - description = "the TCP port of the virtual machine where the network traffic will be load balanced to") + description = "The TCP port of the Instance where the network traffic will be load balanced to") private Integer instancePort; - @Parameter(name = ApiConstants.SOURCE_IP, type = CommandType.STRING, description = "the source IP address the network traffic will be load balanced from") + @Parameter(name = ApiConstants.SOURCE_IP, type = CommandType.STRING, description = "The source IP address the network traffic will be load balanced from") private String sourceIp; @Parameter(name = ApiConstants.SOURCE_IP_NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, - description = "the network id of the source ip address") + description = "The Network ID of the source IP address") private Long sourceIpNetworkId; @Parameter(name = ApiConstants.SCHEME, type = CommandType.STRING, required = true, - description = "the load balancer scheme. Supported value in this release is Internal") + description = "The load balancer scheme. Supported value in this release is Internal") private String scheme; - @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}) + @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}) private Boolean display; ///////////////////////////////////////////////////// @@ -193,7 +193,7 @@ public long getEntityOwnerId() { public void execute() throws ResourceAllocationException, ResourceUnavailableException { ApplicationLoadBalancerRule rule = null; try { - CallContext.current().setEventDetails("Load Balancer Id: " + getEntityId()); + CallContext.current().setEventDetails("Load Balancer ID: " + getEntityUuid()); // State might be different after the rule is applied, so get new object here rule = _entityMgr.findById(ApplicationLoadBalancerRule.class, getEntityId()); ApplicationLoadBalancerResponse lbResponse = _responseGenerator.createLoadBalancerContainerReponse(rule, _lbService.getLbInstances(getEntityId())); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBHealthCheckPolicyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBHealthCheckPolicyCmd.java index c24a5f19f077..7b5cda13f1a5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBHealthCheckPolicyCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBHealthCheckPolicyCmd.java @@ -53,10 +53,10 @@ public class CreateLBHealthCheckPolicyCmd extends BaseAsyncCreateCmd { type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, - description = "the ID of the load balancer rule") + description = "The ID of the load balancer rule") private Long lbRuleId; - @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "the description of the load balancer health check policy") + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "The description of the load balancer health check policy") private String description; @Parameter(name = ApiConstants.HEALTHCHECK_PINGPATH, type = CommandType.STRING, required = false, description = "HTTP ping path") @@ -77,16 +77,16 @@ public class CreateLBHealthCheckPolicyCmd extends BaseAsyncCreateCmd { @Parameter(name = ApiConstants.HEALTHCHECK_HEALTHY_THRESHOLD, type = CommandType.INTEGER, required = false, - description = "Number of consecutive health check success before declaring an instance healthy") + description = "Number of consecutive health check success before declaring an Instance healthy") private int healthyThreshold; @Parameter(name = ApiConstants.HEALTHCHECK_UNHEALTHY_THRESHOLD, type = CommandType.INTEGER, required = false, - description = "Number of consecutive health check failures before declaring an instance unhealthy") + description = "Number of consecutive health check failures before declaring an Instance unhealthy") private int unhealthyThreshold; - @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}) + @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}) private Boolean display; @@ -155,7 +155,7 @@ public void execute() throws ResourceAllocationException, ResourceUnavailableExc boolean success = false; try { - CallContext.current().setEventDetails("Load balancer health check policy ID : " + getEntityId()); + CallContext.current().setEventDetails("Load balancer health check policy ID : " + getEntityUuid()); success = _lbService.applyLBHealthCheckPolicy(this); if (success) { // State might be different after the rule is applied, so get new object here diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBStickinessPolicyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBStickinessPolicyCmd.java index c6b5036bc955..e816e0f95ebb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBStickinessPolicyCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBStickinessPolicyCmd.java @@ -54,25 +54,25 @@ public class CreateLBStickinessPolicyCmd extends BaseAsyncCreateCmd { type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, - description = "the ID of the load balancer rule") + description = "The ID of the load balancer rule") private Long lbRuleId; - @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "the description of the load balancer stickiness policy") + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "The description of the load balancer stickiness policy") private String description; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "name of the load balancer stickiness policy") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the load balancer stickiness policy") private String lbStickinessPolicyName; @Parameter(name = ApiConstants.METHOD_NAME, type = CommandType.STRING, required = true, - description = "name of the load balancer stickiness policy method, possible values are LbCookie, AppCookie, SourceBased") + description = "Name of the load balancer stickiness policy method, possible values are LbCookie, AppCookie, SourceBased") private String stickinessMethodName; - @Parameter(name = ApiConstants.PARAM_LIST, type = CommandType.MAP, description = "param list. Example: param[0].name=cookiename¶m[0].value=LBCookie ") + @Parameter(name = ApiConstants.PARAM_LIST, type = CommandType.MAP, description = "Param list. Example: param[0].name=cookiename¶m[0].value=LBCookie ") private Map paramList; - @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}) + @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}) private Boolean display; @@ -138,7 +138,7 @@ public void execute() throws ResourceAllocationException, ResourceUnavailableExc boolean success = false; try { - CallContext.current().setEventDetails("Rule Id: " + getEntityId()); + CallContext.current().setEventDetails("Rule ID: " + getEntityUuid()); success = _lbService.applyLBStickinessPolicy(this); if (success) { // State might be different after the rule is applied, so get new object here 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 aa43b9cfdaf1..bd72f248364e 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 @@ -58,65 +58,65 @@ public class CreateLoadBalancerRuleCmd extends BaseAsyncCreateCmd /*implements L //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ALGORITHM, type = CommandType.STRING, required = true, description = "load balancer algorithm (source, roundrobin, leastconn)") + @Parameter(name = ApiConstants.ALGORITHM, type = CommandType.STRING, required = true, description = "Load balancer algorithm (source, roundrobin, leastconn)") private String algorithm; - @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "the description of the load balancer rule", length = 4096) + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "The description of the load balancer rule", length = 4096) private String description; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "name of the load balancer rule") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the load balancer rule") private String loadBalancerRuleName; @Parameter(name = ApiConstants.PRIVATE_PORT, type = CommandType.INTEGER, required = true, - description = "the private port of the private IP address/virtual machine where the network traffic will be load balanced to") + description = "The private port of the private IP address/Instance where the network traffic will be load balanced to") private Integer privatePort; @Parameter(name = ApiConstants.PUBLIC_IP_ID, type = CommandType.UUID, entityType = IPAddressResponse.class, - description = "public IP address ID from where the network traffic will be load balanced from") + description = "Public IP address ID from where the network traffic will be load balanced from") private Long publicIpId; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = false, - description = "zone where the load balancer is going to be created. This parameter is required when LB service provider is ElasticLoadBalancerVm") + description = "Zone where the load balancer is going to be created. This parameter is required when LB service provider is ElasticLoadBalancerVm") private Long zoneId; @Parameter(name = ApiConstants.PUBLIC_PORT, type = CommandType.INTEGER, required = true, - description = "the public port from where the network traffic will be load balanced from") + description = "The public port from where the network traffic will be load balanced from") private Integer publicPort; - @Parameter(name = ApiConstants.OPEN_FIREWALL, type = CommandType.BOOLEAN, description = "if true, firewall rule for" + @Parameter(name = ApiConstants.OPEN_FIREWALL, type = CommandType.BOOLEAN, description = "If true, firewall rule for" + " source/end public port is automatically created; if false - firewall rule has to be created explicitly. If not specified 1) defaulted to false when LB" + " rule is being created for VPC guest network 2) in all other cases defaulted to true") private Boolean openFirewall; @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, - description = "the account associated with the load balancer. Must be used with the domainId parameter.") + description = "The Account associated with the load balancer. Must be used with the domainId parameter.") private String accountName; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "the domain ID associated with the load balancer") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "The domain ID associated with the load balancer") private Long domainId; - @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, since = "4.18.0.0", description = "the source CIDR list to allow traffic from; " + @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, since = "4.18.0.0", description = "The source CIDR list to allow traffic from; " + "all other CIDRs will be blocked. Multiple entries must be separated by a single comma character (,). By default, all CIDRs are allowed.") private List cidrlist; - @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "The guest network this " - + "rule will be created for. Required when public Ip address is not associated with any Guest network yet (VPC case)") + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "The guest Network this " + + "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, tcp-proxy or ssl.") + @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}) + @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}) private Boolean display; ///////////////////////////////////////////////////// @@ -207,7 +207,7 @@ public long getNetworkId() { } else { Network defaultGuestNetwork = _networkService.getExclusiveGuestNetwork(zoneId); if (defaultGuestNetwork == null) { - throw new InvalidParameterValueException("Unable to find a default guest network for account " + getAccountName() + " in domain ID=" + getDomainId()); + throw new InvalidParameterValueException("Unable to find a default guest Network for Account " + getAccountName() + " in domain ID=" + getDomainId()); } else { return defaultGuestNetwork.getId(); } @@ -217,7 +217,7 @@ public long getNetworkId() { if (ipAddr.getAssociatedWithNetworkId() != null) { return ipAddr.getAssociatedWithNetworkId(); } else { - throw new InvalidParameterValueException("IP address ID=" + publicIpId + " is not associated with any network"); + throw new InvalidParameterValueException("IP address ID=" + publicIpId + " is not associated with any Network"); } } } @@ -268,7 +268,7 @@ public void execute() throws ResourceAllocationException, ResourceUnavailableExc boolean success = true; LoadBalancer rule = null; try { - CallContext.current().setEventDetails("Rule Id: " + getEntityId()); + CallContext.current().setEventDetails("Rule ID: " + getEntityUuid()); if (getOpenFirewall()) { success = success && _firewallService.applyIngressFirewallRules(getSourceIpAddressId(), callerContext.getCallingAccount()); @@ -340,10 +340,10 @@ public long getAccountId() { if (account != null) { return account.getId(); } else { - throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain ID=" + domainId); + throw new InvalidParameterValueException("Unable to find Account " + accountName + " in domain ID=" + domainId); } } else { - throw new InvalidParameterValueException("Can't define IP owner. Either specify account/domainId or publicIpId"); + throw new InvalidParameterValueException("Can't define IP owner. Either specify Account/domainId or publicIpId"); } } @@ -388,7 +388,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "creating load balancer: " + getName() + " account: " + getAccountName(); + return "Creating load balancer: " + getName() + " Account: " + getAccountName(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/DeleteApplicationLoadBalancerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/DeleteApplicationLoadBalancerCmd.java index 410df086393c..8cfd1876325a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/DeleteApplicationLoadBalancerCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/DeleteApplicationLoadBalancerCmd.java @@ -39,7 +39,7 @@ public class DeleteApplicationLoadBalancerCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "the ID of the Load Balancer") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "The ID of the Load Balancer") private Long id; ///////////////////////////////////////////////////// @@ -71,12 +71,12 @@ public String getEventType() { @Override public String getEventDescription() { - return "deleting load balancer: " + getId(); + return "Deleting load balancer with ID: " + getResourceUuid(ApiConstants.ID); } @Override public void execute() { - CallContext.current().setEventDetails("Load balancer ID: " + getId()); + CallContext.current().setEventDetails("Load balancer ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _appLbService.deleteApplicationLoadBalancer(getId()); if (result) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/DeleteLBHealthCheckPolicyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/DeleteLBHealthCheckPolicyCmd.java index 3cf1f345037a..c01c5a4ca01e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/DeleteLBHealthCheckPolicyCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/DeleteLBHealthCheckPolicyCmd.java @@ -44,7 +44,7 @@ public class DeleteLBHealthCheckPolicyCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = LBHealthCheckResponse.class, required = true, - description = "the ID of the load balancer health check policy") + description = "The ID of the load balancer health check policy") private Long id; // /////////////////////////////////////////////////// @@ -76,12 +76,12 @@ public String getEventType() { @Override public String getEventDescription() { - return "deleting load balancer health check policy: " + getId(); + return "Deleting load balancer health check policy with ID: " + getResourceUuid(ApiConstants.ID); } @Override public void execute() { - CallContext.current().setEventDetails("Load balancer health check policy Id: " + getId()); + CallContext.current().setEventDetails("Load balancer health check policy ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _lbService.deleteLBHealthCheckPolicy(getId(), true); if (result) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/DeleteLBStickinessPolicyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/DeleteLBStickinessPolicyCmd.java index 5d04de3cae58..f26382478f4e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/DeleteLBStickinessPolicyCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/DeleteLBStickinessPolicyCmd.java @@ -45,7 +45,7 @@ public class DeleteLBStickinessPolicyCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = LBStickinessResponse.class, required = true, - description = "the ID of the LB stickiness policy") + description = "The ID of the LB stickiness policy") private Long id; // /////////////////////////////////////////////////// @@ -82,12 +82,12 @@ public String getEventType() { @Override public String getEventDescription() { - return "deleting load balancer stickiness policy: " + getId(); + return "Deleting load balancer stickiness policy with ID: " + getResourceUuid(ApiConstants.ID); } @Override public void execute() { - CallContext.current().setEventDetails("Load balancer stickiness policy ID: " + getId()); + CallContext.current().setEventDetails("Load balancer stickiness policy ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _lbService.deleteLBStickinessPolicy(getId(), true); if (result) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/DeleteLoadBalancerRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/DeleteLoadBalancerRuleCmd.java index b4079430ee32..a41808ced397 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/DeleteLoadBalancerRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/DeleteLoadBalancerRuleCmd.java @@ -44,7 +44,7 @@ public class DeleteLoadBalancerRuleCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, - description = "the ID of the load balancer rule") + description = "The ID of the load balancer rule") private Long id; ///////////////////////////////////////////////////// @@ -76,12 +76,12 @@ public String getEventType() { @Override public String getEventDescription() { - return "deleting load balancer: " + getId(); + return "Deleting load balancer with ID: " + getResourceUuid(ApiConstants.ID); } @Override public void execute() { - CallContext.current().setEventDetails("Load balancer ID: " + getId()); + CallContext.current().setEventDetails("Load balancer ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _firewallService.revokeRelatedFirewallRule(id, true); result = result && _lbService.deleteLoadBalancerRule(id, true); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListApplicationLoadBalancersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListApplicationLoadBalancersCmd.java index d54f3e1155ec..b1d29f18f1a2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListApplicationLoadBalancersCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListApplicationLoadBalancersCmd.java @@ -43,28 +43,28 @@ public class ListApplicationLoadBalancersCmd extends BaseListTaggedResourcesCmd // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, description = "the ID of the load balancer") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, description = "The ID of the load balancer") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the load balancer") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the load balancer") private String loadBalancerName; - @Parameter(name = ApiConstants.SOURCE_IP, type = CommandType.STRING, description = "the source IP address of the load balancer") + @Parameter(name = ApiConstants.SOURCE_IP, type = CommandType.STRING, description = "The source IP address of the load balancer") private String sourceIp; @Parameter(name = ApiConstants.SOURCE_IP_NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, - description = "the network ID of the source IP address") + description = "The network ID of the source IP address") private Long sourceIpNetworkId; - @Parameter(name = ApiConstants.SCHEME, type = CommandType.STRING, description = "the scheme of the load balancer. Supported value is internal in the current release") + @Parameter(name = ApiConstants.SCHEME, type = CommandType.STRING, description = "The scheme of the load balancer. Supported value is internal in the current release") private String scheme; - @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "the network ID of the load balancer") + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "The network ID of the load balancer") private Long networkId; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; // /////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListLBHealthCheckPoliciesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListLBHealthCheckPoliciesCmd.java index cb2cdb446d1c..e91f315bdaf1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListLBHealthCheckPoliciesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListLBHealthCheckPoliciesCmd.java @@ -44,13 +44,13 @@ public class ListLBHealthCheckPoliciesCmd extends BaseListCmd { @Parameter(name = ApiConstants.LBID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, - description = "the ID of the load balancer rule") + description = "The ID of the load balancer rule") private Long lbRuleId; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = LBHealthCheckResponse.class, description = "the ID of the health check policy", since = "4.4") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = LBHealthCheckResponse.class, description = "The ID of the health check policy", since = "4.4") private Long id; // /////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListLBStickinessPoliciesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListLBStickinessPoliciesCmd.java index a48e2ea37b71..a14f731435b1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListLBStickinessPoliciesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListLBStickinessPoliciesCmd.java @@ -45,17 +45,17 @@ public class ListLBStickinessPoliciesCmd extends BaseListCmd { @Parameter(name = ApiConstants.LBID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, - description = "the ID of the load balancer rule") + description = "The ID of the load balancer rule") private Long lbRuleId; @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = LBStickinessResponse.class, - description = "the ID of the load balancer stickiness policy") + description = "The ID of the load balancer stickiness policy") private Long id; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; // /////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListLoadBalancerRuleInstancesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListLoadBalancerRuleInstancesCmd.java index 3bfc68a95bad..bf4604612b1d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListLoadBalancerRuleInstancesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListLoadBalancerRuleInstancesCmd.java @@ -36,7 +36,7 @@ import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; -@APICommand(name = "listLoadBalancerRuleInstances", description = "List all virtual machine instances that are assigned to a load balancer rule.", responseObject = LoadBalancerRuleVmMapResponse.class, responseView = ResponseView.Restricted, +@APICommand(name = "listLoadBalancerRuleInstances", description = "List all Instances that are assigned to a load balancer rule.", responseObject = LoadBalancerRuleVmMapResponse.class, responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class ListLoadBalancerRuleInstancesCmd extends BaseListCmd implements UserCmd { @@ -49,20 +49,20 @@ public class ListLoadBalancerRuleInstancesCmd extends BaseListCmd implements Use @Parameter(name = ApiConstants.APPLIED, type = CommandType.BOOLEAN, - description = "true if listing all virtual machines currently applied to the load balancer rule; default is true") + description = "True if listing all Instances currently applied to the load balancer rule; default is true") private Boolean applied; @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, - description = "the ID of the load balancer rule") + description = "The ID of the load balancer rule") private Long id; @Parameter(name = ApiConstants.LIST_LB_VMIPS, type = CommandType.BOOLEAN, - description = "true if load balancer rule VM IP information to be included; default is false") + description = "True if load balancer rule Instance IP information to be included; default is false") private boolean isListLbVmip; @@ -95,10 +95,10 @@ public String getCommandName() { public void execute() { Pair, List> vmServiceMap = _lbService.listLoadBalancerInstances(this); List result = vmServiceMap.first(); - logger.debug(String.format("A total of [%s] user VMs were obtained when listing the load balancer instances: [%s].", result.size(), result)); + logger.debug("A total of [{}] user VMs were obtained when listing the load balancer instances: [{}].", result.size(), result); List serviceStates = vmServiceMap.second(); - logger.debug(String.format("A total of [%s] service states were obtained when listing the load balancer instances: [%s].", serviceStates.size(), serviceStates)); + logger.debug("A total of [{}] service states were obtained when listing the load balancer instances: [{}].", serviceStates.size(), serviceStates); if (!isListLbVmip()) { ListResponse response = new ListResponse<>(); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListLoadBalancerRulesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListLoadBalancerRulesCmd.java index b8b82f0c4a89..6ee317567776 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListLoadBalancerRulesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListLoadBalancerRulesCmd.java @@ -44,31 +44,31 @@ public class ListLoadBalancerRulesCmd extends BaseListTaggedResourcesCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, description = "the ID of the load balancer rule") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, description = "The ID of the load balancer rule") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the load balancer rule") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the load balancer rule") private String loadBalancerRuleName; @Parameter(name = ApiConstants.PUBLIC_IP_ID, type = CommandType.UUID, entityType = IPAddressResponse.class, - description = "the public IP address ID of the load balancer rule") + description = "The public IP address ID of the load balancer rule") private Long publicIpId; @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, - description = "the ID of the virtual machine of the load balancer rule") + description = "The ID of the Instance of the load balancer rule") private Long virtualMachineId; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the availability zone ID") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The availability zone ID") private Long zoneId; - @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "list by network ID the rule belongs to") + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "List by Network ID the rule belongs to") private Long networkId; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; // /////////////////////////////////////////////////// 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 ddd2133d932e..010a5ad6022d 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 @@ -45,7 +45,7 @@ public class RemoveCertFromLoadBalancerCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, - description = "the ID of the load balancer rule") + description = "The ID of the load balancer rule") Long lbRuleId; @Override @@ -67,7 +67,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Removing a certificate from a load balancer with ID " + getLbRuleId(); + return "Removing certificate from load balancer with ID " + getResourceUuid(ApiConstants.LBID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/RemoveFromLoadBalancerRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/RemoveFromLoadBalancerRuleCmd.java index d29f2676ed55..ffcafd47822c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/RemoveFromLoadBalancerRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/RemoveFromLoadBalancerRuleCmd.java @@ -43,7 +43,7 @@ import com.cloud.user.Account; @APICommand(name = "removeFromLoadBalancerRule", - description = "Removes a virtual machine or a list of virtual machines from a load balancer rule.", + description = "Removes an Instance or a list of Instances from a load balancer rule.", responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -65,7 +65,7 @@ public class RemoveFromLoadBalancerRuleCmd extends BaseAsyncCmd { type = CommandType.LIST, collectionType = CommandType.UUID, entityType = UserVmResponse.class, - description = "the list of IDs of the virtual machines that are being removed from the load balancer rule (i.e. virtualMachineIds=1,2,3)") + description = "The list of IDs of the Instances that are being removed from the load balancer rule (i.e. virtualMachineIds=1,2,3)") private List virtualMachineIds; @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID_IP, @@ -121,7 +121,7 @@ public Map> getVmIdIpListMap() { VirtualMachine lbvm = _entityMgr.findByUuid(VirtualMachine.class, vmId); if (lbvm == null) { - throw new InvalidParameterValueException("Unable to find virtual machine ID: " + vmId); + throw new InvalidParameterValueException("Unable to find Instance ID: " + vmId); } Long longVmId = lbvm.getId(); @@ -143,12 +143,12 @@ public Map> getVmIdIpListMap() { @Override public String getEventDescription() { - return "removing instances from load balancer: " + getId() + " (ids: " + StringUtils.join(getVirtualMachineIds(), ",") + ")"; + return "Removing Instances from load balancer with ID: " + getResourceUuid(ApiConstants.ID) + " (instances IDs: " + StringUtils.join(getVirtualMachineIds(), ",") + ")"; } @Override public void execute() { - CallContext.current().setEventDetails("Load balancer Id: " + getId() + " VmIds: " + StringUtils.join(getVirtualMachineIds(), ",")); + CallContext.current().setEventDetails("Load balancer ID: " + getResourceUuid(ApiConstants.ID) + " Instances IDs: " + StringUtils.join(getVirtualMachineIds(), ",")); Map> vmIdIpsMap = getVmIdIpListMap(); try { boolean result = _lbService.removeFromLoadBalancer(id, virtualMachineIds, vmIdIpsMap, false); @@ -156,10 +156,10 @@ public void execute() { SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove instance from load balancer rule"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove Instance from load balancer rule"); } }catch (InvalidParameterValueException ex) { - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Failed to remove instance from load balancer rule"); + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Failed to remove Instance from load balancer rule"); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UpdateApplicationLoadBalancerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UpdateApplicationLoadBalancerCmd.java index d129cd8988f8..c2075c2c79e0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UpdateApplicationLoadBalancerCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UpdateApplicationLoadBalancerCmd.java @@ -38,10 +38,10 @@ public class UpdateApplicationLoadBalancerCmd extends BaseAsyncCustomIdCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "the ID of the load balancer") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "The ID of the load balancer") private Long id; - @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}) + @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}) private Boolean display; ///////////////////////////////////////////////////// @@ -72,7 +72,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "updating load balancer: " + getId(); + return "Updating load balancer with ID: " + getResourceUuid(ApiConstants.ID); } @@ -81,7 +81,7 @@ public String getEventDescription() { ///////////////////////////////////////////////////// @Override public void execute() { - CallContext.current().setEventDetails("Load balancer ID: " + getId()); + CallContext.current().setEventDetails("Load balancer ID: " + getResourceUuid(ApiConstants.ID)); ApplicationLoadBalancerRule rule = _appLbService.updateApplicationLoadBalancer(getId(), this.getCustomId(), getDisplay()); ApplicationLoadBalancerResponse lbResponse = _responseGenerator.createLoadBalancerContainerReponse(rule, _lbService.getLbInstances(getId())); setResponseObject(lbResponse); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UpdateLBHealthCheckPolicyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UpdateLBHealthCheckPolicyCmd.java index fdd98fc3a0a4..17bd61c502dc 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UpdateLBHealthCheckPolicyCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UpdateLBHealthCheckPolicyCmd.java @@ -37,7 +37,7 @@ public class UpdateLBHealthCheckPolicyCmd extends BaseAsyncCustomIdCmd{ @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = LBHealthCheckResponse.class, required = true, description = "ID of load balancer health check policy") private Long id; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the policy to the end user or not", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "An optional field, whether to the display the policy to the end user or not", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; ///////////////////////////////////////////////////// @@ -63,7 +63,7 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - return "Update load balancer health check policy ID= " + id; + return "Update load balancer health check policy ID = " + id; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UpdateLBStickinessPolicyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UpdateLBStickinessPolicyCmd.java index b2137cf262d8..e79c1c561206 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UpdateLBStickinessPolicyCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UpdateLBStickinessPolicyCmd.java @@ -33,10 +33,10 @@ public class UpdateLBStickinessPolicyCmd extends BaseAsyncCustomIdCmd{ ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = LBStickinessResponse.class, required = true, description = "id of lb stickiness policy") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = LBStickinessResponse.class, required = true, description = "ID of lb stickiness policy") private Long id; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the policy to the end user or not", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "An optional field, whether to the display the policy to the end user or not", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; ///////////////////////////////////////////////////// @@ -62,7 +62,7 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - return "Update load balancer stickiness policy ID= " + id; + return "Update load balancer stickiness policy ID = " + id; } @Override 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 25254ba9eb75..0ac99f1c760c 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.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) @@ -42,28 +43,31 @@ public class UpdateLoadBalancerRuleCmd extends BaseAsyncCustomIdCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ALGORITHM, type = CommandType.STRING, description = "load balancer algorithm (source, roundrobin, leastconn)") + @Parameter(name = ApiConstants.ALGORITHM, type = CommandType.STRING, description = "Load balancer algorithm (source, roundrobin, leastconn)") private String algorithm; - @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "the description of the load balancer rule", length = 4096) + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "The description of the load balancer rule", length = 4096) private String description; @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, - description = "the ID of the load balancer rule to update") + description = "The ID of the load balancer rule to update") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the load balancer rule") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the load balancer rule") private String loadBalancerName; - @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}) + @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}) private Boolean display; @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 String getLbProtocol() { return lbProtocol; } + public List getCidrList() { + return cidrList; + } ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -112,12 +119,12 @@ public String getEventType() { @Override public String getEventDescription() { - return "updating load balancer rule"; + return "Updating load balancer rule with ID: " + getResourceUuid(ApiConstants.ID); } @Override public void execute() { - CallContext.current().setEventDetails("Load balancer ID: " + getId()); + CallContext.current().setEventDetails("Load balancer ID: " + getResourceUuid(ApiConstants.ID)); LoadBalancer result = _lbService.updateLoadBalancerRule(this); if (result != null) { LoadBalancerResponse response = _responseGenerator.createLoadBalancerResponse(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UploadSslCertCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UploadSslCertCmd.java index e51b4dee9db6..0032b7a0acdf 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UploadSslCertCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UploadSslCertCmd.java @@ -61,13 +61,13 @@ public class UploadSslCertCmd extends BaseCmd { @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, description = "Password for the private key") private String password; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "account that will own the SSL certificate") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "Account that will own the SSL certificate") private String accountName; - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "an optional project for the SSL certificate") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "An optional project for the SSL certificate") private Long projectId; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "domain ID of the account owning the SSL certificate") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "Domain ID of the account owning the SSL certificate") private Long domainId; @Parameter(name = ApiConstants.NAME , type = CommandType.STRING, required = true, description = "Name for the uploaded certificate") diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/nat/CreateIpForwardingRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/nat/CreateIpForwardingRuleCmd.java index e883a7a0e4dd..98487ddeb19a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/nat/CreateIpForwardingRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/nat/CreateIpForwardingRuleCmd.java @@ -54,24 +54,24 @@ public class CreateIpForwardingRuleCmd extends BaseAsyncCreateCmd implements Sta type = CommandType.UUID, entityType = IPAddressResponse.class, required = true, - description = "the public IP address ID of the forwarding rule, already associated via associateIp") + description = "The public IP address ID of the forwarding rule, already associated via associateIp") private Long ipAddressId; - @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, required = true, description = "the start port for the rule") + @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, required = true, description = "The start port for the rule") private Integer startPort; - @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "the end port for the rule") + @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "The end port for the rule") private Integer endPort; - @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, required = true, description = "the protocol for the rule. Valid values are TCP or UDP.") + @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, required = true, description = "The protocol for the rule. Valid values are TCP or UDP.") private String protocol; @Parameter(name = ApiConstants.OPEN_FIREWALL, type = CommandType.BOOLEAN, - description = "if true, firewall rule for source/end public port is automatically created; if false - firewall rule has to be created explicitly. Has value true by default") + description = "If true, firewall rule for source/end public port is automatically created; if false - firewall rule has to be created explicitly. Has value true by default") private Boolean openFirewall; - @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the CIDR list to forward traffic from. Multiple entries must be separated by a single comma character (,). This parameter is deprecated. Do not use.") + @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "The CIDR list to forward traffic from. Multiple entries must be separated by a single comma character (,). This parameter is deprecated. Do not use.") private List cidrlist; ///////////////////////////////////////////////////// @@ -108,7 +108,7 @@ public void execute() throws ResourceUnavailableException { boolean result = true; FirewallRule rule = null; try { - CallContext.current().setEventDetails("Rule ID: " + getEntityId()); + CallContext.current().setEventDetails("Rule ID: " + getEntityUuid()); if (getOpenFirewall()) { result = result && _firewallService.applyIngressFirewallRules(ipAddressId, CallContext.current().getCallingAccount()); @@ -148,7 +148,7 @@ public void create() { setEntityId(rule.getId()); setEntityUuid(rule.getUuid()); } catch (NetworkRuleConflictException e) { - logger.info("Unable to create static NAT rule due to ", e); + logger.error("Unable to create static NAT rule due to ", e); throw new ServerApiException(ApiErrorCode.NETWORK_RULE_CONFLICT_ERROR, e.getMessage()); } } @@ -172,14 +172,14 @@ public String getEventType() { @Override public String getEventDescription() { IpAddress ip = _networkService.getIp(ipAddressId); - return ("Applying an ipforwarding 1:1 NAT rule for IP: " + ip.getAddress() + " with virtual machine:" + getVirtualMachineId()); + return ("Applying an IP forwarding 1:1 NAT rule for IP: " + ip.getAddress() + " with Instance:" + getVirtualMachineId()); } private long getVirtualMachineId() { Long vmId = _networkService.getIp(ipAddressId).getAssociatedWithVmId(); if (vmId == null) { - throw new InvalidParameterValueException("IP address is not associated with any network, unable to create static NAT rule"); + throw new InvalidParameterValueException("IP address is not associated with any Network, unable to create static NAT rule"); } return vmId; } @@ -229,8 +229,13 @@ public FirewallRule.State getState() { } @Override - public long getNetworkId() { - return -1; + public Long getNetworkId() { + return -1L; + } + + @Override + public Long getVpcId() { + return null; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/nat/DeleteIpForwardingRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/nat/DeleteIpForwardingRuleCmd.java index e4c16a317518..ef9f428f8c8c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/nat/DeleteIpForwardingRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/nat/DeleteIpForwardingRuleCmd.java @@ -42,7 +42,7 @@ public class DeleteIpForwardingRuleCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "the ID of the forwarding rule") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "The ID of the forwarding rule") private Long id; // unexposed parameter needed for events logging @@ -63,7 +63,7 @@ public Long getId() { @Override public void execute() { - CallContext.current().setEventDetails("Rule ID: " + id); + CallContext.current().setEventDetails("Rule ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _firewallService.revokeRelatedFirewallRule(id, true); result = result && _rulesService.revokeStaticNatRule(id, true); @@ -95,7 +95,7 @@ public String getEventType() { @Override public String getEventDescription() { - return ("Deleting an IP forwarding 1:1 NAT rule ID:" + id); + return "Deleting IP forwarding 1:1 NAT rule with ID:" + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/nat/DisableStaticNatCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/nat/DisableStaticNatCmd.java index 2bee7dfcc895..d80d63541c0f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/nat/DisableStaticNatCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/nat/DisableStaticNatCmd.java @@ -46,7 +46,7 @@ public class DisableStaticNatCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = IPAddressResponse.class, required = true, - description = "the public IP address ID for which static NAT feature is being disabled") + description = "The public IP address ID for which static NAT feature is being disabled") private Long ipAddressId; ///////////////////////////////////////////////////// @@ -67,7 +67,7 @@ public String getEventType() { @Override public String getEventDescription() { - return ("Disabling static NAT for IP ID=" + ipAddressId); + return ("Disabling static NAT for IP with ID: " + getResourceUuid(ApiConstants.IP_ADDRESS_ID)); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/nat/EnableStaticNatCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/nat/EnableStaticNatCmd.java index 48c6cc20bf1c..23ad2852a8e3 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/nat/EnableStaticNatCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/nat/EnableStaticNatCmd.java @@ -45,19 +45,19 @@ public class EnableStaticNatCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.IP_ADDRESS_ID, type = CommandType.UUID, entityType = IPAddressResponse.class, required = true, description = "the public IP " + @Parameter(name = ApiConstants.IP_ADDRESS_ID, type = CommandType.UUID, entityType = IPAddressResponse.class, required = true, description = "The public IP " + "address ID for which static NAT feature is being enabled") private Long ipAddressId; - @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, description = "the ID of " - + "the virtual machine for enabling static NAT feature") + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, description = "The ID of " + + "the Instance for enabling static NAT feature") private Long virtualMachineId; @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, - description = "The network of the VM the static NAT will be enabled for." - + " Required when public IP address is not associated with any guest network yet (VPC case)") + description = "The Network of the Instance the static NAT will be enabled for." + + " Required when public IP address is not associated with any guest Network yet (VPC case)") private Long networkId; @Parameter(name = ApiConstants.VM_GUEST_IP, type = CommandType.STRING, @@ -101,7 +101,7 @@ public long getNetworkId() { if (ntwkId == null) { throw new InvalidParameterValueException("Unable to enable static NAT for the ipAddress id=" + ipAddressId + - " as IP is not associated with any network and no networkId is passed in"); + " as IP is not associated with any Network and no networkId is passed in"); } return ntwkId; } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/nat/ListIpForwardingRulesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/nat/ListIpForwardingRulesCmd.java index 89981a6453b5..089ed972b3bb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/nat/ListIpForwardingRulesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/nat/ListIpForwardingRulesCmd.java @@ -46,7 +46,7 @@ public class ListIpForwardingRulesCmd extends BaseListProjectAndAccountResources @Parameter(name = ApiConstants.IP_ADDRESS_ID, type = CommandType.UUID, entityType = IPAddressResponse.class, - description = "list the rule belonging to this public IP address") + description = "List the rule belonging to this public IP address") private Long publicIpAddressId; @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, description = "Lists rule with the specified ID.") @@ -55,7 +55,7 @@ public class ListIpForwardingRulesCmd extends BaseListProjectAndAccountResources @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, - description = "Lists all rules applied to the specified VM.") + description = "Lists all rules applied to the specified Instance.") private Long vmId; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkACLCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkACLCmd.java index 8d8e598bcab8..1776436b31a3 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkACLCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkACLCmd.java @@ -40,7 +40,7 @@ import com.cloud.user.Account; import com.cloud.utils.net.NetUtils; -@APICommand(name = "createNetworkACL", description = "Creates a ACL rule in the given network (the network has to belong to VPC)", responseObject = NetworkACLItemResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +@APICommand(name = "createNetworkACL", description = "Creates a ACL rule in the given Network (the Network has to belong to VPC)", responseObject = NetworkACLItemResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateNetworkACLCmd extends BaseAsyncCreateCmd { @@ -48,40 +48,40 @@ public class CreateNetworkACLCmd extends BaseAsyncCreateCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, required = true, description = "the protocol for the ACL rule. Valid values are TCP/UDP/ICMP/ALL or valid protocol number") + @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, required = true, description = "The protocol for the ACL rule. Valid values are TCP/UDP/ICMP/ALL or valid protocol number") private String protocol; - @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "the starting port of ACL") + @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "The starting port of ACL") private Integer publicStartPort; - @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "the ending port of ACL") + @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "The ending port of ACL") private Integer publicEndPort; - @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the CIDR list to allow traffic from/to. Multiple entries must be separated by a single comma character (,).") - private List cidrlist; + @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "The CIDR list to allow traffic from/to. Multiple entries must be separated by a single comma character (,).") + private List cidrList; - @Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "type of the ICMP message being sent") + @Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "Type of the ICMP message being sent") private Integer icmpType; - @Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "error code for this ICMP message") + @Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "Error code for this ICMP message") private Integer icmpCode; - @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "The network of the VM the ACL will be created for") + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "The Network of the Instance the ACL will be created for") private Long networkId; - @Parameter(name = ApiConstants.ACL_ID, type = CommandType.UUID, entityType = NetworkACLResponse.class, description = "The network of the VM the ACL will be created for") + @Parameter(name = ApiConstants.ACL_ID, type = CommandType.UUID, entityType = NetworkACLResponse.class, description = "The network of the Instance the ACL will be created for") private Long aclId; - @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, description = "the traffic type for the ACL," + "can be ingress or egress, defaulted to ingress if not specified") + @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, description = "The traffic type for the ACL," + "can be ingress or egress, defaulted to ingress if not specified") private String trafficType; @Parameter(name = ApiConstants.NUMBER, type = CommandType.INTEGER, description = "The number of the ACL item, its ordering") private Integer number; - @Parameter(name = ApiConstants.ACTION, type = CommandType.STRING, description = "scl entry action, allow or deny") + @Parameter(name = ApiConstants.ACTION, type = CommandType.STRING, description = "ACL entry action, allow or deny") private String action; - @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 = { + @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}) private Boolean display; @@ -118,8 +118,8 @@ public void setProtocol(String protocol) { } public List getSourceCidrList() { - if (cidrlist != null) { - return cidrlist; + if (cidrList != null) { + return cidrList; } else { List oneCidrList = new ArrayList(); oneCidrList.add(NetUtils.ALL_IP4_CIDRS); @@ -238,6 +238,30 @@ public String getReason() { return reason; } + public void setCidrList(List cidrList) { + this.cidrList = cidrList; + } + + public void setIcmpType(Integer icmpType) { + this.icmpType = icmpType; + } + + public void setIcmpCode(Integer icmpCode) { + this.icmpCode = icmpCode; + } + + public void setNumber(Integer number) { + this.number = number; + } + + public void setDisplay(Boolean display) { + this.display = display; + } + + public void setReason(String reason) { + this.reason = reason; + } + @Override public void create() { NetworkACLItem result = _networkACLService.createNetworkACLItem(this); @@ -250,7 +274,7 @@ public void execute() throws ResourceUnavailableException { boolean success = false; NetworkACLItem rule = _networkACLService.getNetworkACLItem(getEntityId()); try { - CallContext.current().setEventDetails("Rule ID: " + getEntityId()); + CallContext.current().setEventDetails("Rule ID: " + getEntityUuid()); success = _networkACLService.applyNetworkACL(rule.getAclId()); // State is different after the rule is applied, so get new object here diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkACLListCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkACLListCmd.java index cd25a604e776..31d1b49c2e71 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkACLListCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkACLListCmd.java @@ -36,7 +36,7 @@ import com.cloud.network.vpc.Vpc; import com.cloud.user.Account; -@APICommand(name = "createNetworkACLList", description = "Creates a network ACL. If no VPC is given, then it creates a global ACL that can be used by everyone.", +@APICommand(name = "createNetworkACLList", description = "Creates a Network ACL. If no VPC is given, then it creates a global ACL that can be used by everyone.", responseObject = NetworkACLResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateNetworkACLListCmd extends BaseAsyncCreateCmd { @@ -46,19 +46,19 @@ public class CreateNetworkACLListCmd extends BaseAsyncCreateCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the network ACL list") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the Network ACL list") private String name; - @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "Description of the network ACL list") + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "Description of the Network ACL list") private String description; @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, - description = "ID of the VPC associated with this network ACL list") + description = "ID of the VPC associated with this Network ACL list") private Long vpcId; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the list to the end user or not", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "An optional field, whether to the display the list to the end User or not", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; // /////////////////////////////////////////////////// @@ -112,7 +112,7 @@ public void execute() throws ResourceUnavailableException { setResponseObject(aclResponse); aclResponse.setResponseName(getCommandName()); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create network ACL"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Network ACL"); } } @@ -128,7 +128,7 @@ public long getEntityOwnerId() { } else { account = CallContext.current().getCallingAccount(); if (!Account.Type.ADMIN.equals(account.getType())) { - logger.warn(String.format("Only Root Admin can create global ACLs. Account [%s] cannot create any global ACL.", account)); + logger.error("Only Root Admin can create global ACLs. {} cannot create any global ACL.", account); throw new PermissionDeniedException("Only Root Admin can create global ACLs."); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java index aca3d3ca1b45..79fb5f6d01cf 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java @@ -57,68 +57,68 @@ public class CreateNetworkCmd extends BaseCmd implements UserCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the network") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "The name of the network") private String name; - @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "the display text of the network") + @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "The display text of the network") private String displayText; @Parameter(name = ApiConstants.NETWORK_OFFERING_ID, type = CommandType.UUID, entityType = NetworkOfferingResponse.class, required = true, - description = "the network offering ID") + description = "The network offering ID") private Long networkOfferingId; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "the zone ID for the network") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "The zone ID for the network") private Long zoneId; @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, - description = "the physical network ID the network belongs to") + description = "The physical Network ID the network belongs to") private Long physicalNetworkId; - @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, description = "the gateway of the network. Required " + @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, description = "The gateway of the Network. Required " + "for shared networks and isolated networks when it belongs to VPC") private String gateway; - @Parameter(name = ApiConstants.NETMASK, type = CommandType.STRING, description = "the netmask of the network. Required " + @Parameter(name = ApiConstants.NETMASK, type = CommandType.STRING, description = "The netmask of the Network. Required " + "for shared networks and isolated networks when it belongs to VPC") private String netmask; - @Parameter(name = ApiConstants.START_IP, type = CommandType.STRING, description = "the beginning IP address in the network IP range") + @Parameter(name = ApiConstants.START_IP, type = CommandType.STRING, description = "The beginning IP address in the Network IP range") private String startIp; - @Parameter(name = ApiConstants.END_IP, type = CommandType.STRING, description = "the ending IP address in the network IP" + @Parameter(name = ApiConstants.END_IP, type = CommandType.STRING, description = "The ending IP address in the Network IP" + " range. If not specified, will be defaulted to startIP") private String endIp; - @Parameter(name = ApiConstants.ISOLATED_PVLAN, type = CommandType.STRING, description = "the isolated private VLAN for this network") + @Parameter(name = ApiConstants.ISOLATED_PVLAN, type = CommandType.STRING, description = "The isolated private VLAN for this Network") private String isolatedPvlan; @Parameter(name = ApiConstants.ISOLATED_PVLAN_TYPE, type = CommandType.STRING, - description = "the isolated private VLAN type for this network") + description = "The isolated private VLAN type for this network") private String isolatedPvlanType; - @Parameter(name = ApiConstants.NETWORK_DOMAIN, type = CommandType.STRING, description = "network domain") + @Parameter(name = ApiConstants.NETWORK_DOMAIN, type = CommandType.STRING, description = "Network domain") private String networkDomain; @Parameter(name = ApiConstants.ACL_TYPE, type = CommandType.STRING, description = "Access control type; supported values" - + " are account and domain. In 3.0 all shared networks should have aclType=Domain, and all isolated networks" - + " - Account. Account means that only the account owner can use the network, domain - all accounts in the domain can use the network") + + " are Account and domain. In 3.0 all shared networks should have aclType=Domain, and all isolated networks" + + " - Account. Account means that only the Account owner can use the network, domain - all accounts in the domain can use the network") private String aclType; @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "Account that will own the network. Account should be under the selected domain") private String accountName; - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "an optional project for the network") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "An optional project for the network") private Long projectId; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "domain ID of the account owning a network. " + - "If the account is not specified, but the acltype is Account or not specified, the network will be automatically assigned to the caller account and domain. " + - "To create a network under the domain without linking it to any account, make sure to include acltype=Domain parameter in the api call. " + - "If account is not specified, but acltype is Domain, the network will be created for the specified domain.") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "Domain ID of the account owning a network. " + + "If the Account is not specified, but the acltype is Account or not specified, the Network will be automatically assigned to the caller account and domain. " + + "To create a Network under the domain without linking it to any account, make sure to include acltype=Domain parameter in the api call. " + + "If Account is not specified, but acltype is Domain, the network will be created for the specified domain.") private Long domainId; @Parameter(name = ApiConstants.SUBDOMAIN_ACCESS, @@ -127,22 +127,22 @@ public class CreateNetworkCmd extends BaseCmd implements UserCmd { + " subdomains to use networks dedicated to their parent domain(s). Should be used with aclType=Domain, defaulted to allow.subdomain.network.access global config if not specified") private Boolean subdomainAccess; - @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, description = "the VPC network belongs to") + @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, description = "The VPC network belongs to") private Long vpcId; @Parameter(name = ApiConstants.TUNGSTEN_VIRTUAL_ROUTER_UUID, type = CommandType.STRING, description = "Tungsten-Fabric virtual router the network belongs to") private String tungstenVirtualRouterUuid; - @Parameter(name = ApiConstants.START_IPV6, type = CommandType.STRING, description = "the beginning IPv6 address in the IPv6 network range") + @Parameter(name = ApiConstants.START_IPV6, type = CommandType.STRING, description = "The beginning IPv6 address in the IPv6 network range") private String startIpv6; - @Parameter(name = ApiConstants.END_IPV6, type = CommandType.STRING, description = "the ending IPv6 address in the IPv6 network range") + @Parameter(name = ApiConstants.END_IPV6, type = CommandType.STRING, description = "The ending IPv6 address in the IPv6 network range") private String endIpv6; - @Parameter(name = ApiConstants.IP6_GATEWAY, type = CommandType.STRING, description = "the gateway of the IPv6 network. Required for Shared networks") + @Parameter(name = ApiConstants.IP6_GATEWAY, type = CommandType.STRING, description = "The gateway of the IPv6 network. Required for Shared networks") private String ip6Gateway; - @Parameter(name = ApiConstants.IP6_CIDR, type = CommandType.STRING, description = "the CIDR of IPv6 network, must be at least /64") + @Parameter(name = ApiConstants.IP6_CIDR, type = CommandType.STRING, description = "The CIDR of IPv6 network, must be at least /64") private String ip6Cidr; @Parameter(name = ApiConstants.EXTERNAL_ID, type = CommandType.STRING, description = "ID of the network in an external system.") @@ -150,7 +150,7 @@ public class CreateNetworkCmd extends BaseCmd implements UserCmd { @Parameter(name = ApiConstants.DISPLAY_NETWORK, type = CommandType.BOOLEAN, - description = "an optional field, whether to the display the network to the end user or not.", authorized = {RoleType.Admin}) + description = "An optional field, whether to the display the network to the end User or not.", authorized = {RoleType.Admin}) private Boolean displayNetwork; @Parameter(name = ApiConstants.ACL_ID, type = CommandType.UUID, entityType = NetworkACLResponse.class, description = "Network ACL ID associated for the network") @@ -171,16 +171,16 @@ public class CreateNetworkCmd extends BaseCmd implements UserCmd { description = "MTU to be configured on the network VR's private interface(s)", since = "4.18.0") private Integer privateMtu; - @Parameter(name = ApiConstants.DNS1, type = CommandType.STRING, description = "the first IPv4 DNS for the network", since = "4.18.0") + @Parameter(name = ApiConstants.DNS1, type = CommandType.STRING, description = "The first IPv4 DNS for the network", since = "4.18.0") private String ip4Dns1; - @Parameter(name = ApiConstants.DNS2, type = CommandType.STRING, description = "the second IPv4 DNS for the network", since = "4.18.0") + @Parameter(name = ApiConstants.DNS2, type = CommandType.STRING, description = "The second IPv4 DNS for the network", since = "4.18.0") private String ip4Dns2; - @Parameter(name = ApiConstants.IP6_DNS1, type = CommandType.STRING, description = "the first IPv6 DNS for the network", since = "4.18.0") + @Parameter(name = ApiConstants.IP6_DNS1, type = CommandType.STRING, description = "The first IPv6 DNS for the network", since = "4.18.0") private String ip6Dns1; - @Parameter(name = ApiConstants.IP6_DNS2, type = CommandType.STRING, description = "the second IPv6 DNS for the network", since = "4.18.0") + @Parameter(name = ApiConstants.IP6_DNS2, type = CommandType.STRING, description = "The second IPv6 DNS for the network", since = "4.18.0") private String ip6Dns2; @Parameter(name = ApiConstants.SOURCE_NAT_IP, @@ -199,6 +199,11 @@ public class CreateNetworkCmd extends BaseCmd implements UserCmd { @Parameter(name=ApiConstants.AS_NUMBER, type=CommandType.LONG, since = "4.20.0", description="the AS Number of the network") private Long asNumber; + @Parameter(name = ApiConstants.KEEP_MAC_ADDRESS_ON_PUBLIC_NIC, + description = ApiConstants.PARAMETER_DESCRIPTION_KEEP_MAC_ADDRESS_ON_PUBLIC_NIC, + type = CommandType.BOOLEAN, since = "4.23.0", authorized = {RoleType.Admin}) + private Boolean keepMacAddressOnPublicNic; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -286,6 +291,10 @@ public String getSourceNatIP() { return sourceNatIP; } + public Boolean getKeepMacAddressOnPublicNic() { + return keepMacAddressOnPublicNic; + } + @Override public boolean isDisplay() { if(displayNetwork == null) @@ -410,6 +419,27 @@ public Long getAsNumber() { ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// + + public CreateNetworkCmd() { + } + + public CreateNetworkCmd(long networkOfferingId, String name, String displayText, String gateway, String netmask, String startIp, String endIp, long domainId, + String accountName, long zoneId, String aclType, boolean subdomainAccess, boolean displayNetwork) { + this.networkOfferingId = networkOfferingId; + this.name = name; + this.displayText = displayText; + this.gateway = gateway; + this.netmask = netmask; + this.startIp = startIp; + this.endIp = endIp; + this.domainId = domainId; + this.accountName = accountName; + this.zoneId = zoneId; + this.aclType = aclType; + this.subdomainAccess = subdomainAccess; + this.displayNetwork = displayNetwork; + } + @Override public String getCommandName() { return s_name; @@ -417,7 +447,7 @@ public String getCommandName() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkPermissionsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkPermissionsCmd.java index 1df472cbb228..eb95bc781892 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkPermissionsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkPermissionsCmd.java @@ -51,24 +51,24 @@ public class CreateNetworkPermissionsCmd extends BaseCmd { @Parameter(name = ApiConstants.ACCOUNTS, type = CommandType.LIST, collectionType = CommandType.STRING, - description = "a comma delimited list of accounts within owner's domain. If specified, \"op\" parameter has to be passed in.") + description = "A comma delimited list of accounts within owner's domain. If specified, \"op\" parameter has to be passed in.") private List accountNames; @Parameter(name = ApiConstants.ACCOUNT_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AccountResponse.class, - description = "a comma delimited list of account IDs within owner's domain. If specified, \"op\" parameter has to be passed in.") + description = "A comma delimited list of account IDs within owner's domain. If specified, \"op\" parameter has to be passed in.") private List accountIds; - @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "the network ID") + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "The network ID") private Long networkId; @Parameter(name = ApiConstants.PROJECT_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = ProjectResponse.class, - description = "a comma delimited list of projects within owner's domain. If specified, \"op\" parameter has to be passed in.") + description = "A comma delimited list of projects within owner's domain. If specified, \"op\" parameter has to be passed in.") private List projectIds; // /////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/DeleteNetworkACLCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/DeleteNetworkACLCmd.java index ca42626eacb5..a8506e06c4a3 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/DeleteNetworkACLCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/DeleteNetworkACLCmd.java @@ -32,7 +32,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.user.Account; -@APICommand(name = "deleteNetworkACL", description = "Deletes a network ACL", responseObject = SuccessResponse.class, +@APICommand(name = "deleteNetworkACL", description = "Deletes a Network ACL", responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DeleteNetworkACLCmd extends BaseAsyncCmd { @@ -40,7 +40,7 @@ public class DeleteNetworkACLCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = NetworkACLItemResponse.class, required = true, description = "the ID of the network ACL") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = NetworkACLItemResponse.class, required = true, description = "The ID of the Network ACL") private Long id; ///////////////////////////////////////////////////// @@ -61,7 +61,7 @@ public String getEventType() { @Override public String getEventDescription() { - return ("Deleting Network ACL ID=" + id); + return "Deleting Network ACL with ID:" + getResourceUuid(ApiConstants.ID); } @Override @@ -82,14 +82,14 @@ public ApiCommandResourceType getApiResourceType() { @Override public void execute() throws ResourceUnavailableException { - CallContext.current().setEventDetails("Network ACL item ID: " + id); + CallContext.current().setEventDetails("Network ACL item ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _networkACLService.revokeNetworkACLItem(id); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete network ACL item"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete Network ACL item"); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/DeleteNetworkACLListCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/DeleteNetworkACLListCmd.java index 45bc86e8c91d..3e3894a26864 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/DeleteNetworkACLListCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/DeleteNetworkACLListCmd.java @@ -40,7 +40,7 @@ public class DeleteNetworkACLListCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = NetworkACLResponse.class, required = true, description = "the ID of the network ACL") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = NetworkACLResponse.class, required = true, description = "The ID of the network ACL") private Long id; ///////////////////////////////////////////////////// @@ -61,7 +61,7 @@ public String getEventType() { @Override public String getEventDescription() { - return ("Deleting network ACL ID=" + id); + return ("Deleting network ACL with ID: " + getResourceUuid(ApiConstants.ID)); } @Override @@ -82,7 +82,7 @@ public ApiCommandResourceType getApiResourceType() { @Override public void execute() throws ResourceUnavailableException { - CallContext.current().setEventDetails("Network ACL ID: " + id); + CallContext.current().setEventDetails("Network ACL ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _networkACLService.deleteNetworkACL(id); if (result) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/DeleteNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/DeleteNetworkCmd.java index 8e8e18c67024..0543794e8bf5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/DeleteNetworkCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/DeleteNetworkCmd.java @@ -33,7 +33,7 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.network.Network; -@APICommand(name = "deleteNetwork", description = "Deletes a network", responseObject = SuccessResponse.class, entityType = {Network.class}, +@APICommand(name = "deleteNetwork", description = "Deletes a Network", responseObject = SuccessResponse.class, entityType = {Network.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DeleteNetworkCmd extends BaseAsyncCmd { @@ -41,7 +41,7 @@ public class DeleteNetworkCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "the ID of the network") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "The ID of the network") private Long id; @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force delete a network." + @@ -66,7 +66,7 @@ public boolean isForced() { @Override public void execute() { - CallContext.current().setEventDetails("Network Id: " + id); + CallContext.current().setEventDetails("Network ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _networkService.deleteNetwork(id, isForced()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); @@ -93,7 +93,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Deleting network: " + id; + return "Deleting network with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ImportNetworkACLCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ImportNetworkACLCmd.java new file mode 100644 index 000000000000..daf3633ce2a4 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ImportNetworkACLCmd.java @@ -0,0 +1,132 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.network; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.NetworkACLItemResponse; +import org.apache.cloudstack.api.response.NetworkACLResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.commons.collections.MapUtils; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.vpc.NetworkACLItem; +import com.cloud.user.Account; + +@APICommand(name = "importNetworkACL", description = "Imports Network ACL rules.", + responseObject = NetworkACLItemResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.22.1") +public class ImportNetworkACLCmd extends BaseAsyncCmd { + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter( + name = ApiConstants.ACL_ID, + type = CommandType.UUID, + entityType = NetworkACLResponse.class, + required = true, + description = "The ID of the Network ACL to which the rules will be imported" + ) + private Long aclId; + + @Parameter(name = ApiConstants.RULES, type = CommandType.MAP, required = true, + description = "Rules param list, id and protocol are must. Invalid rules will be discarded. Example: " + + "rules[0].id=101&rules[0].protocol=tcp&rules[0].traffictype=ingress&rules[0].state=active&rules[0].cidrlist=192.168.1.0/24" + + "&rules[0].tags=web&rules[0].aclid=acl-001&rules[0].aclname=web-acl&rules[0].number=1&rules[0].action=allow&rules[0].fordisplay=true" + + "&rules[0].description=allow%20web%20traffic&rules[1].id=102&rules[1].protocol=udp&rules[1].traffictype=egress&rules[1].state=enabled" + + "&rules[1].cidrlist=10.0.0.0/8&rules[1].tags=db&rules[1].aclid=acl-002&rules[1].aclname=db-acl&rules[1].number=2&rules[1].action=deny" + + "&rules[1].fordisplay=false&rules[1].description=deny%20database%20traffic") + private Map rules; + + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + // Returns map, corresponds to a rule with the details in the keys: + // id, protocol, startport, endport, traffictype, state, cidrlist, tags, aclid, aclname, number, action, fordisplay, description + public Map getRules() { + return rules; + } + + public Long getAclId() { + return aclId; + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + + @Override + public void execute() throws ResourceUnavailableException { + validateParams(); + List importedRules = _networkACLService.importNetworkACLRules(this); + ListResponse response = new ListResponse<>(); + List aclResponse = new ArrayList<>(); + for (NetworkACLItem acl : importedRules) { + NetworkACLItemResponse ruleData = _responseGenerator.createNetworkACLItemResponse(acl); + aclResponse.add(ruleData); + } + response.setResponses(aclResponse, importedRules.size()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + Account account = CallContext.current().getCallingAccount(); + if (account != null) { + return account.getId(); + } + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_NETWORK_ACL_IMPORT; + } + + @Override + public String getEventDescription() { + return "Importing ACL rules for ACL ID: " + getAclId(); + } + + + private void validateParams() { + if(MapUtils.isEmpty(rules)) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Rules parameter is empty or null"); + } + + if (getAclId() == null || _networkACLService.getNetworkACL(getAclId()) == null) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to find Network ACL with provided ACL ID"); + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkACLListsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkACLListsCmd.java index c88f956943b4..62834a51e033 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkACLListsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkACLListsCmd.java @@ -43,16 +43,16 @@ public class ListNetworkACLListsCmd extends BaseListProjectAndAccountResourcesCm @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = NetworkACLResponse.class, description = "Lists network ACL with the specified ID.") private Long id; - @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "list network ACLs by network ID") + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "List network ACLs by network ID") private Long networkId; - @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, description = "list network ACLs by VPC ID") + @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, description = "List network ACLs by VPC ID") private Long vpcId; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "list network ACLs by specified name") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "List network ACLs by specified name") private String name; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkACLsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkACLsCmd.java index 1ef2b9b7bfbf..159f07296eef 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkACLsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkACLsCmd.java @@ -44,22 +44,22 @@ public class ListNetworkACLsCmd extends BaseListTaggedResourcesCmd { description = "Lists network ACL Item with the specified ID") private Long id; - @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "list network ACL items by network ID") + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "List network ACL items by network ID") private Long networkId; - @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, description = "list network ACL items by traffic type - ingress or egress") + @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, description = "List network ACL items by traffic type - ingress or egress") private String trafficType; - @Parameter(name = ApiConstants.ACL_ID, type = CommandType.UUID, entityType = NetworkACLResponse.class, description = "list network ACL items by ACL ID") + @Parameter(name = ApiConstants.ACL_ID, type = CommandType.UUID, entityType = NetworkACLResponse.class, description = "List network ACL items by ACL ID") private Long aclId; - @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "list network ACL items by protocol") + @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "List network ACL items by protocol") private String protocol; - @Parameter(name = ApiConstants.ACTION, type = CommandType.STRING, description = "list network ACL items by action") + @Parameter(name = ApiConstants.ACTION, type = CommandType.STRING, description = "List network ACL items by action") private String action; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkOfferingsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkOfferingsCmd.java index bdc89d804cdf..ff3b61056be3 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkOfferingsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkOfferingsCmd.java @@ -39,75 +39,75 @@ public class ListNetworkOfferingsCmd extends BaseListCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = NetworkOfferingResponse.class, description = "list network offerings by ID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = NetworkOfferingResponse.class, description = "List network offerings by ID") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "list network offerings by name") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "List network offerings by name") private String networkOfferingName; - @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "list network offerings by display text") + @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "List network offerings by display text") private String displayText; - @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, description = "list by traffic type") + @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, description = "List by traffic type") private String trafficType; - @Parameter(name = ApiConstants.IS_DEFAULT, type = CommandType.BOOLEAN, description = "true if need to list only default network offerings. Default value is false") + @Parameter(name = ApiConstants.IS_DEFAULT, type = CommandType.BOOLEAN, description = "True if need to list only default network offerings. Default value is false") private Boolean isDefault; - @Parameter(name = ApiConstants.SPECIFY_VLAN, type = CommandType.BOOLEAN, description = "the tags for the network offering.") + @Parameter(name = ApiConstants.SPECIFY_VLAN, type = CommandType.BOOLEAN, description = "The tags for the network offering.") private Boolean specifyVlan; - @Parameter(name = ApiConstants.AVAILABILITY, type = CommandType.STRING, description = "the availability of network offering. Default value is required") + @Parameter(name = ApiConstants.AVAILABILITY, type = CommandType.STRING, description = "The availability of network offering. Default value is required") private String availability; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "list network offerings available for network creation in specific domain", + description = "List network offerings available for network creation in specific domain", since = "4.13") private Long domainId; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, - description = "list network offerings available for network creation in specific zone") + description = "List network offerings available for network creation in specific zone") private Long zoneId; - @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "list network offerings by state") + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "List network offerings by state") private String state; @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, - description = "the ID of the network. Pass this in if you want to see the available network offering that a network can be changed to.") + description = "The ID of the network. Pass this in if you want to see the available network offering that a network can be changed to.") private Long networkId; - @Parameter(name = ApiConstants.GUEST_IP_TYPE, type = CommandType.STRING, description = "list network offerings by guest type: shared or isolated") + @Parameter(name = ApiConstants.GUEST_IP_TYPE, type = CommandType.STRING, description = "List network offerings by guest type: shared or isolated") private String guestIpType; @Parameter(name = ApiConstants.SUPPORTED_SERVICES, type = CommandType.LIST, collectionType = CommandType.STRING, - description = "list network offerings supporting certain services") + description = "List network offerings supporting certain services") private List supportedServices; @Parameter(name = ApiConstants.SOURCE_NAT_SUPPORTED, type = CommandType.BOOLEAN, - description = "true if need to list only netwok offerings where source NAT is supported, false otherwise") + description = "True if need to list only netwok offerings where source NAT is supported, false otherwise") private Boolean sourceNatSupported; @Parameter(name = ApiConstants.SPECIFY_IP_RANGES, type = CommandType.BOOLEAN, - description = "true if need to list only network offerings which support specifying ip ranges") + description = "True if need to list only network offerings which support specifying ip ranges") private Boolean specifyIpRanges; - @Parameter(name = ApiConstants.TAGS, type = CommandType.STRING, description = "list network offerings by tags", length = 4096) + @Parameter(name = ApiConstants.TAGS, type = CommandType.STRING, description = "List network offerings by tags", length = 4096) private String tags; - @Parameter(name = ApiConstants.IS_TAGGED, type = CommandType.BOOLEAN, description = "true if offering has tags specified") + @Parameter(name = ApiConstants.IS_TAGGED, type = CommandType.BOOLEAN, description = "True if offering has tags specified") private Boolean isTagged; - @Parameter(name = ApiConstants.FOR_VPC, type = CommandType.BOOLEAN, description = "the network offering can be used" + " only for network creation inside the VPC") + @Parameter(name = ApiConstants.FOR_VPC, type = CommandType.BOOLEAN, description = "The network offering can be used" + " only for network creation inside the VPC") private Boolean forVpc; @Parameter(name = ApiConstants.ROUTING_MODE, diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkPermissionsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkPermissionsCmd.java index 6ea4937e1153..e852fdccecb9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkPermissionsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkPermissionsCmd.java @@ -34,7 +34,7 @@ import java.util.ArrayList; import java.util.List; -@APICommand(name = "listNetworkPermissions", description = "List network visibility and all accounts that have permissions to view this network.", +@APICommand(name = "listNetworkPermissions", description = "List Network visibility and all Accounts that have permissions to view this Network.", responseObject = NetworkPermissionsResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, @@ -47,7 +47,7 @@ public class ListNetworkPermissionsCmd extends BaseCmd implements UserCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "Lists network permission by network ID") + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "Lists Network permission by Network ID") private Long networkId; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworksCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworksCmd.java index 0e8425b14b4b..79f2cf8c7449 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworksCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworksCmd.java @@ -42,7 +42,7 @@ import com.cloud.utils.Pair; import org.apache.commons.lang3.StringUtils; -@APICommand(name = "listNetworks", description = "Lists all available networks.", responseObject = NetworkResponse.class, responseView = ResponseView.Restricted, entityType = {Network.class}, +@APICommand(name = "listNetworks", description = "Lists all available Networks.", responseObject = NetworkResponse.class, responseView = ResponseView.Restricted, entityType = {Network.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListNetworksCmd extends BaseListRetrieveOnlyResourceCountCmd implements UserCmd { private static final String s_name = "listnetworksresponse"; @@ -50,49 +50,52 @@ public class ListNetworksCmd extends BaseListRetrieveOnlyResourceCountCmd implem ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "list networks by ID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "List Networks by ID") private Long id; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the zone ID of the network") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "list networks by name", since = "4.22.0") + private String name; + + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The zone ID of the Network") private Long zoneId; - @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "the type of the network. Supported values are: isolated, l2, shared and all") + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "The type of the Network. Supported values are: isolated, l2, shared and all") private String guestIpType; - @Parameter(name = ApiConstants.IS_SYSTEM, type = CommandType.BOOLEAN, description = "true if network is system, false otherwise") + @Parameter(name = ApiConstants.IS_SYSTEM, type = CommandType.BOOLEAN, description = "True if Network is system, false otherwise") private Boolean isSystem; - @Parameter(name = ApiConstants.ACL_TYPE, type = CommandType.STRING, description = "list networks by ACL (access control list) type. Supported values are account and domain") + @Parameter(name = ApiConstants.ACL_TYPE, type = CommandType.STRING, description = "List Networks by ACL (access control list) type. Supported values are Account and domain") private String aclType; - @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, description = "type of the traffic") + @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, description = "Type of the traffic") private String trafficType; - @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, description = "list networks by physical network id") + @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, description = "List Networks by physical Network ID") private Long physicalNetworkId; - @Parameter(name = ApiConstants.SUPPORTED_SERVICES, type = CommandType.LIST, collectionType = CommandType.STRING, description = "list networks supporting certain services") + @Parameter(name = ApiConstants.SUPPORTED_SERVICES, type = CommandType.LIST, collectionType = CommandType.STRING, description = "List Networks supporting certain services") private List supportedServices; - @Parameter(name = ApiConstants.RESTART_REQUIRED, type = CommandType.BOOLEAN, description = "list networks by restartRequired") + @Parameter(name = ApiConstants.RESTART_REQUIRED, type = CommandType.BOOLEAN, description = "List networks by restartRequired") private Boolean restartRequired; - @Parameter(name = ApiConstants.SPECIFY_IP_RANGES, type = CommandType.BOOLEAN, description = "true if need to list only networks which support specifying IP ranges") + @Parameter(name = ApiConstants.SPECIFY_IP_RANGES, type = CommandType.BOOLEAN, description = "True if need to list only networks which support specifying IP ranges") private Boolean specifyIpRanges; @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, description = "List networks by VPC") private Long vpcId; - @Parameter(name = ApiConstants.CAN_USE_FOR_DEPLOY, type = CommandType.BOOLEAN, description = "list networks available for VM deployment") + @Parameter(name = ApiConstants.CAN_USE_FOR_DEPLOY, type = CommandType.BOOLEAN, description = "List networks available for Instance deployment") private Boolean canUseForDeploy; - @Parameter(name = ApiConstants.FOR_VPC, type = CommandType.BOOLEAN, description = "the network belongs to VPC") + @Parameter(name = ApiConstants.FOR_VPC, type = CommandType.BOOLEAN, description = "The network belongs to VPC") private Boolean forVpc; - @Parameter(name = ApiConstants.DISPLAY_NETWORK, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.DISPLAY_NETWORK, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; - @Parameter(name = ApiConstants.NETWORK_OFFERING_ID, type = CommandType.UUID, entityType = NetworkOfferingResponse.class, description = "list networks by network offering ID") + @Parameter(name = ApiConstants.NETWORK_OFFERING_ID, type = CommandType.UUID, entityType = NetworkOfferingResponse.class, description = "List networks by network offering ID") private Long networkOfferingId; @Parameter(name = ApiConstants.ASSOCIATED_NETWORK_ID, @@ -103,17 +106,17 @@ public class ListNetworksCmd extends BaseListRetrieveOnlyResourceCountCmd implem private Long associatedNetworkId; @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, - description = "flag to display the resource icon for networks") + description = "Flag to display the resource icon for networks") private Boolean showIcon; @Parameter(name = ApiConstants.NETWORK_FILTER, type = CommandType.STRING, since = "4.17.0", - description = "possible values are \"account\", \"domain\", \"accountdomain\",\"shared\", and \"all\". Default value is \"all\"." - + "* account : account networks that have been registered for or created by the calling user. " - + "* domain : domain networks that have been registered for or created by the calling user. " - + "* accountdomain : account and domain networks that have been registered for or created by the calling user. " - + "* shared : networks that have been granted to the calling user by another user. " + description = "Possible values are \"account\", \"domain\", \"accountdomain\",\"shared\", and \"all\". Default value is \"all\"." + + "* account : account networks that have been registered for or created by the calling User. " + + "* domain : domain networks that have been registered for or created by the calling User. " + + "* accountdomain : account and domain networks that have been registered for or created by the calling User. " + + "* shared : networks that have been granted to the calling User by another User. " + "* all : all networks (account, domain and shared).") private String networkFilter; @@ -125,6 +128,10 @@ public Long getId() { return id; } + public String getName() { + return name; + } + public Long getZoneId() { return zoneId; } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/MoveNetworkAclItemCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/MoveNetworkAclItemCmd.java index 5d36dcfd8e93..6b525e63c185 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/MoveNetworkAclItemCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/MoveNetworkAclItemCmd.java @@ -28,7 +28,7 @@ import com.cloud.network.vpc.NetworkACLItem; import com.cloud.user.Account; -@APICommand(name = "moveNetworkAclItem", description = "Move an ACL rule to a position bettwen two other ACL rules of the same ACL network list", responseObject = NetworkACLItemResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +@APICommand(name = "moveNetworkAclItem", description = "Move an ACL rule to a position between two other ACL rules of the same ACL Network list", responseObject = NetworkACLItemResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class MoveNetworkAclItemCmd extends BaseAsyncCustomIdCmd { private static final String s_name = "moveNetworkAclItemResponse"; @@ -42,7 +42,7 @@ public class MoveNetworkAclItemCmd extends BaseAsyncCustomIdCmd { @Parameter(name = ApiConstants.NEXT_ACL_RULE_ID, type = CommandType.STRING, description = "The ID of the rule that is right after the new position where the rule being moved is going to be placed. This value can be 'NULL' if the rule is being moved to the last position of the network ACL list.") private String nextAclRuleUuid; - @Parameter(name = ApiConstants.MOVE_ACL_CONSISTENCY_HASH, type = CommandType.STRING, description = "Md5 hash used to check the consistency of the ACL rule list before applying the ACL rule move. This check is useful to manage concurrency problems that may happen when multiple users are editing the same ACL rule listing. The parameter is not required. Therefore, if the user does not send it, they assume the risk of moving ACL rules without checking the consistency of the access control list before executing the move. We use MD5 hash function on a String that is composed of all UUIDs of the ACL rules in concatenated in their respective order (order defined via 'number' field).") + @Parameter(name = ApiConstants.MOVE_ACL_CONSISTENCY_HASH, type = CommandType.STRING, description = "Md5 hash used to check the consistency of the ACL rule list before applying the ACL rule move. This check is useful to manage concurrency problems that may happen when multiple Users are editing the same ACL rule listing. The parameter is not required. Therefore, if the User does not send it, they assume the risk of moving ACL rules without checking the consistency of the access control list before executing the move. We use MD5 hash function on a String that is composed of all UUIDs of the ACL rules in concatenated in their respective order (order defined via 'number' field).") private String aclConsistencyHash; @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/RemoveNetworkPermissionsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/RemoveNetworkPermissionsCmd.java index c199d872e0c0..546b460f16c5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/RemoveNetworkPermissionsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/RemoveNetworkPermissionsCmd.java @@ -34,7 +34,7 @@ import java.util.List; -@APICommand(name = "removeNetworkPermissions", description = "Removes network permissions.", +@APICommand(name = "removeNetworkPermissions", description = "Removes Network permissions.", responseObject = SuccessResponse.class, entityType = {Network.class}, requestHasSensitiveInfo = false, @@ -51,24 +51,24 @@ public class RemoveNetworkPermissionsCmd extends BaseCmd { @Parameter(name = ApiConstants.ACCOUNTS, type = CommandType.LIST, collectionType = CommandType.STRING, - description = "a comma delimited list of accounts within owner's domain. If specified, \"op\" parameter has to be passed in.") + description = "A comma delimited list of Accounts within owner's domain. If specified, \"op\" parameter has to be passed in.") private List accountNames; @Parameter(name = ApiConstants.ACCOUNT_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AccountResponse.class, - description = "a comma delimited list of account IDs within owner's domain. If specified, \"op\" parameter has to be passed in.") + description = "A comma delimited list of account IDs within owner's domain. If specified, \"op\" parameter has to be passed in.") private List accountIds; - @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "the network ID") + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "The Network ID") private Long networkId; @Parameter(name = ApiConstants.PROJECT_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = ProjectResponse.class, - description = "a comma delimited list of projects within owner's domain. If specified, \"op\" parameter has to be passed in.") + description = "A comma delimited list of projects within owner's domain. If specified, \"op\" parameter has to be passed in.") private List projectIds; // /////////////////////////////////////////////////// @@ -105,7 +105,7 @@ public void execute() { SuccessResponse response = new SuccessResponse(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update network permissions"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update Network permissions"); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ReplaceNetworkACLListCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ReplaceNetworkACLListCmd.java index f6e9557aadb3..0b210b6b95d8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ReplaceNetworkACLListCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ReplaceNetworkACLListCmd.java @@ -41,13 +41,13 @@ public class ReplaceNetworkACLListCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ACL_ID, type = CommandType.UUID, entityType = NetworkACLResponse.class, required = true, description = "the ID of the network ACL") + @Parameter(name = ApiConstants.ACL_ID, type = CommandType.UUID, entityType = NetworkACLResponse.class, required = true, description = "The ID of the network ACL") private long aclId; - @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "the ID of the network") + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "The ID of the network") private Long networkId; - @Parameter(name = ApiConstants.GATEWAY_ID, type = CommandType.UUID, entityType = PrivateGatewayResponse.class, description = "the ID of the private gateway") + @Parameter(name = ApiConstants.GATEWAY_ID, type = CommandType.UUID, entityType = PrivateGatewayResponse.class, description = "The ID of the private gateway") private Long privateGatewayId; ///////////////////////////////////////////////////// @@ -76,7 +76,13 @@ public String getEventType() { @Override public String getEventDescription() { - return ("Associating network ACL ID=" + aclId + " with network ID=" + networkId); + String description = "Associating Network ACL with ID:" + getResourceUuid(ApiConstants.ACL_ID); + + if (getNetworkId() != null) { + description += " to Network with ID:" + getResourceUuid(ApiConstants.NETWORK_ID); + } + + return description; } @Override @@ -95,7 +101,7 @@ public void execute() throws ResourceUnavailableException { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Network ID and private gateway can't be passed at the same time"); } - CallContext.current().setEventDetails("Network ACL ID: " + aclId); + CallContext.current().setEventDetails("Network ACL ID: " + getResourceUuid(ApiConstants.ACL_ID)); boolean result = false; if (getPrivateGatewayId() != null) { result = _networkACLService.replaceNetworkACLonPrivateGw(aclId, privateGatewayId); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ResetNetworkPermissionsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ResetNetworkPermissionsCmd.java index a23b98c84a88..611e35028be0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ResetNetworkPermissionsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ResetNetworkPermissionsCmd.java @@ -29,7 +29,7 @@ import com.cloud.network.Network; import com.cloud.user.Account; -@APICommand(name = "resetNetworkPermissions", description = "Resets network permissions.", +@APICommand(name = "resetNetworkPermissions", description = "Resets Network permissions.", responseObject = SuccessResponse.class, entityType = {Network.class}, requestHasSensitiveInfo = false, @@ -43,7 +43,7 @@ public class ResetNetworkPermissionsCmd extends BaseCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "the network ID") + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "The Network ID") private Long networkId; // /////////////////////////////////////////////////// @@ -65,7 +65,7 @@ public void execute() { SuccessResponse response = new SuccessResponse(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update network permissions"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update Network permissions"); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/RestartNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/RestartNetworkCmd.java index ffc2e36dee53..2742e5ef6d18 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/RestartNetworkCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/RestartNetworkCmd.java @@ -37,7 +37,7 @@ import com.cloud.network.Network; @APICommand(name = "restartNetwork", - description = "Restarts the network; includes 1) restarting network elements - virtual routers, DHCP servers 2) reapplying all public IPs 3) reapplying loadBalancing/portForwarding rules", + description = "Restarts the Network; includes 1) restarting network elements - virtual routers, DHCP servers 2) reapplying all public IPs 3) reapplying loadBalancing/portForwarding rules", responseObject = SuccessResponse.class, entityType = {Network.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -115,7 +115,7 @@ public Long getSyncObjId() { @Override public String getEventDescription() { - return "Restarting network: " + getNetworkId(); + return "Restarting Network with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkACLItemCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkACLItemCmd.java index 42cb0697edb4..f1ba3a55a96c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkACLItemCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkACLItemCmd.java @@ -41,37 +41,37 @@ public class UpdateNetworkACLItemCmd extends BaseAsyncCustomIdCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = NetworkACLItemResponse.class, required = true, description = "the ID of the network ACL item") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = NetworkACLItemResponse.class, required = true, description = "The ID of the network ACL item") private Long id; - @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "the protocol for the ACL rule. Valid values are TCP/UDP/ICMP/ALL or valid protocol number") + @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "The protocol for the ACL rule. Valid values are TCP/UDP/ICMP/ALL or valid protocol number") private String protocol; - @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "the starting port of ACL") + @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "The starting port of ACL") private Integer publicStartPort; - @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "the ending port of ACL") + @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "The ending port of ACL") private Integer publicEndPort; - @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the cidr list to allow traffic from/to. Multiple entries must be separated by a single comma character (,).") + @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "The CIDR list to allow traffic from/to. Multiple entries must be separated by a single comma character (,).") private List cidrlist; - @Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "type of the ICMP message being sent") + @Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "Type of the ICMP message being sent") private Integer icmpType; - @Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "error code for this ICMP message") + @Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "Error code for this ICMP message") private Integer icmpCode; - @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, description = "the traffic type for the ACL, can be Ingress or Egress, defaulted to Ingress if not specified") + @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, description = "The traffic type for the ACL, can be Ingress or Egress, defaulted to Ingress if not specified") private String trafficType; - @Parameter(name = ApiConstants.NUMBER, type = CommandType.INTEGER, description = "The network of the vm the ACL will be created for") + @Parameter(name = ApiConstants.NUMBER, type = CommandType.INTEGER, description = "The Network of the Instance the ACL will be created for") private Integer number; - @Parameter(name = ApiConstants.ACTION, type = CommandType.STRING, description = "scl entry action, allow or deny") + @Parameter(name = ApiConstants.ACTION, type = CommandType.STRING, description = "ACL entry action, allow or deny") private String action; - @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 = { + @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}) private Boolean display; @@ -176,7 +176,7 @@ public String getReason() { @Override public void execute() throws ResourceUnavailableException { - CallContext.current().setEventDetails("Rule Id: " + getId()); + CallContext.current().setEventDetails("Rule ID: " + getResourceUuid(ApiConstants.ID)); NetworkACLItem aclItem = _networkACLService.updateNetworkACLItem(this); NetworkACLItemResponse aclResponse = _responseGenerator.createNetworkACLItemResponse(aclItem); setResponseObject(aclResponse); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkACLListCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkACLListCmd.java index adab885542d1..fa29ed44b701 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkACLListCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkACLListCmd.java @@ -31,17 +31,17 @@ import com.cloud.network.vpc.NetworkACL; import com.cloud.user.Account; -@APICommand(name = "updateNetworkACLList", description = "Updates network ACL list", responseObject = SuccessResponse.class, since = "4.4", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +@APICommand(name = "updateNetworkACLList", description = "Updates Network ACL list", responseObject = SuccessResponse.class, since = "4.4", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class UpdateNetworkACLListCmd extends BaseAsyncCustomIdCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = NetworkACLResponse.class, required = true, description = "the ID of the network ACL") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = NetworkACLResponse.class, required = true, description = "The ID of the network ACL") private Long id; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the list to the end user or not", since = "4.4", authorized = { + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "An optional field, whether to the display the list to the end User or not", since = "4.4", authorized = { RoleType.Admin}) private Boolean display; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmd.java index 0d92a635e7f9..7b6841d60976 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmd.java @@ -39,7 +39,7 @@ import com.cloud.network.Network; import com.cloud.offering.NetworkOffering; -@APICommand(name = "updateNetwork", description = "Updates a network", responseObject = NetworkResponse.class, responseView = ResponseView.Restricted, entityType = {Network.class}, +@APICommand(name = "updateNetwork", description = "Updates a Network", responseObject = NetworkResponse.class, responseView = ResponseView.Restricted, entityType = {Network.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class UpdateNetworkCmd extends BaseAsyncCustomIdCmd implements UserCmd { @@ -50,33 +50,33 @@ public class UpdateNetworkCmd extends BaseAsyncCustomIdCmd implements UserCmd { ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType = NetworkResponse.class, - required=true, description="the ID of the network") + required=true, description = "The ID of the network") protected Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the new name for the network") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The new name for the network") private String name; - @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "the new display text for the network") + @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "The new display text for the network") private String displayText; - @Parameter(name = ApiConstants.NETWORK_DOMAIN, type = CommandType.STRING, description = "network domain") + @Parameter(name = ApiConstants.NETWORK_DOMAIN, type = CommandType.STRING, description = "Network domain") private String networkDomain; @Parameter(name = ApiConstants.CHANGE_CIDR, type = CommandType.BOOLEAN, description = "Force update even if CIDR type is different") private Boolean changeCidr; - @Parameter(name = ApiConstants.NETWORK_OFFERING_ID, type = CommandType.UUID, entityType = NetworkOfferingResponse.class, description = "network offering ID") + @Parameter(name = ApiConstants.NETWORK_OFFERING_ID, type = CommandType.UUID, entityType = NetworkOfferingResponse.class, description = "Network offering ID") private Long networkOfferingId; @Parameter(name = ApiConstants.GUEST_VM_CIDR, type = CommandType.STRING, description = "CIDR for guest VMs, CloudStack allocates IPs to guest VMs only from this CIDR") private String guestVmCidr; - @Parameter(name =ApiConstants.UPDATE_IN_SEQUENCE, type=CommandType.BOOLEAN, description = "if true, we will update the routers one after the other. applicable only for redundant router based networks using virtual router as provider") + @Parameter(name =ApiConstants.UPDATE_IN_SEQUENCE, type=CommandType.BOOLEAN, description = "If true, we will update the routers one after the other. applicable only for redundant router based networks using virtual router as provider") private Boolean updateInSequence; @Parameter(name = ApiConstants.DISPLAY_NETWORK, type = CommandType.BOOLEAN, - description = "an optional field, whether to the display the network to the end user or not.", authorized = {RoleType.Admin}) + description = "An optional field, whether to the display the network to the end User or not.", authorized = {RoleType.Admin}) private Boolean displayNetwork; @Parameter(name= ApiConstants.FORCED, type = CommandType.BOOLEAN, description = "Setting this to true will cause a forced network update,", authorized = {RoleType.Admin}) @@ -90,21 +90,26 @@ public class UpdateNetworkCmd extends BaseAsyncCustomIdCmd implements UserCmd { description = "MTU to be configured on the network VR's public facing interfaces", since = "4.18.0") private Integer privateMtu; - @Parameter(name = ApiConstants.DNS1, type = CommandType.STRING, description = "the first IPv4 DNS for the network. Empty string will update the first IPv4 DNS with the value from the zone", since = "4.18.0") + @Parameter(name = ApiConstants.DNS1, type = CommandType.STRING, description = "The first IPv4 DNS for the network. Empty string will update the first IPv4 DNS with the value from the zone", since = "4.18.0") private String ip4Dns1; - @Parameter(name = ApiConstants.DNS2, type = CommandType.STRING, description = "the second IPv4 DNS for the network. Empty string will update the second IPv4 DNS with the value from the zone", since = "4.18.0") + @Parameter(name = ApiConstants.DNS2, type = CommandType.STRING, description = "The second IPv4 DNS for the network. Empty string will update the second IPv4 DNS with the value from the zone", since = "4.18.0") private String ip4Dns2; - @Parameter(name = ApiConstants.IP6_DNS1, type = CommandType.STRING, description = "the first IPv6 DNS for the network. Empty string will update the first IPv6 DNS with the value from the zone", since = "4.18.0") + @Parameter(name = ApiConstants.IP6_DNS1, type = CommandType.STRING, description = "The first IPv6 DNS for the network. Empty string will update the first IPv6 DNS with the value from the zone", since = "4.18.0") private String ip6Dns1; - @Parameter(name = ApiConstants.IP6_DNS2, type = CommandType.STRING, description = "the second IPv6 DNS for the network. Empty string will update the second IPv6 DNS with the value from the zone", since = "4.18.0") + @Parameter(name = ApiConstants.IP6_DNS2, type = CommandType.STRING, description = "The second IPv6 DNS for the network. Empty string will update the second IPv6 DNS with the value from the zone", since = "4.18.0") private String ip6Dns2; @Parameter(name = ApiConstants.SOURCE_NAT_IP, type = CommandType.STRING, description = "IPV4 address to be assigned to the public interface of the network router. This address must already be acquired for this network", since = "4.19") private String sourceNatIP; + @Parameter(name = ApiConstants.KEEP_MAC_ADDRESS_ON_PUBLIC_NIC, + description = ApiConstants.PARAMETER_DESCRIPTION_KEEP_MAC_ADDRESS_ON_PUBLIC_NIC, + type = CommandType.BOOLEAN, since = "4.23.0", authorized = {RoleType.Admin}) + private Boolean keepMacAddressOnPublicNic; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -186,6 +191,10 @@ public String getSourceNatIP() { return sourceNatIP; } + public Boolean getKeepMacAddressOnPublicNic() { + return keepMacAddressOnPublicNic; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/CreateRoutingFirewallRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/CreateRoutingFirewallRuleCmd.java index 7146d1ae1d19..85166f5ab84a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/CreateRoutingFirewallRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/CreateRoutingFirewallRuleCmd.java @@ -238,7 +238,7 @@ public void execute() throws ResourceUnavailableException { boolean success = false; FirewallRule rule = _firewallService.getFirewallRule(getEntityId()); try { - CallContext.current().setEventDetails("Rule ID: " + getEntityId()); + CallContext.current().setEventDetails("Rule ID: " + getEntityUuid()); success = routedIpv4Manager.applyRoutingFirewallRule(rule.getId()); // State is different after the rule is applied, so get new object here @@ -250,7 +250,7 @@ public void execute() throws ResourceUnavailableException { } ruleResponse.setResponseName(getCommandName()); } catch (Exception ex) { - logger.error("Got exception when create Routing firewall rules: " + ex); + logger.error("Got exception when create Routing firewall rules: ", ex); } finally { if (!success || rule == null) { routedIpv4Manager.revokeRoutingFirewallRule(getEntityId()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/DeleteRoutingFirewallRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/DeleteRoutingFirewallRuleCmd.java index 16696f5f71b7..646b704e088c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/DeleteRoutingFirewallRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/DeleteRoutingFirewallRuleCmd.java @@ -67,7 +67,7 @@ public String getEventType() { @Override public String getEventDescription() { - return String.format("Deleting ipv4 routing firewall rule ID=%s", id); + return String.format("Deleting IPv4 routing firewall rule with ID: %s", getResourceUuid(ApiConstants.ID)); } @Override @@ -82,7 +82,7 @@ public long getEntityOwnerId() { @Override public void execute() throws ResourceUnavailableException { - CallContext.current().setEventDetails("Routing firewall rule ID: " + id); + CallContext.current().setEventDetails("Routing firewall rule with ID: " + getResourceUuid(ApiConstants.ID)); boolean result = routedIpv4Manager.revokeRoutingFirewallRule(id); if (result) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/UpdateRoutingFirewallRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/UpdateRoutingFirewallRuleCmd.java index c6f6034b1ba1..3c3a07ceece1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/UpdateRoutingFirewallRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/UpdateRoutingFirewallRuleCmd.java @@ -95,7 +95,7 @@ public String getEventDescription() { @Override public void execute() throws ResourceUnavailableException { - CallContext.current().setEventDetails("Rule Id: " + getId()); + CallContext.current().setEventDetails("Rule ID: " + getResourceUuid(ApiConstants.ID)); FirewallRule rule = routedIpv4Manager.updateRoutingFirewallRule(this); FirewallResponse ruleResponse = _responseGenerator.createFirewallResponse(rule); setResponseObject(ruleResponse); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListDiskOfferingsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListDiskOfferingsCmd.java index f9b9ec59a400..c7343b95dd6b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListDiskOfferingsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListDiskOfferingsCmd.java @@ -45,13 +45,13 @@ public class ListDiskOfferingsCmd extends BaseListProjectAndAccountResourcesCmd @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "ID of the disk offering") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "name of the disk offering") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Name of the disk offering") private String diskOfferingName; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, - description = "id of zone disk offering is associated with", + description = "ID of zone disk offering is associated with", since = "4.13") private Long zoneId; @@ -61,7 +61,7 @@ public class ListDiskOfferingsCmd extends BaseListProjectAndAccountResourcesCmd @Parameter(name = ApiConstants.STORAGE_ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, description = "The ID of the storage pool, tags of the storage pool are used to filter the offerings", since = "4.17") private Long storagePoolId; - @Parameter(name = ApiConstants.ENCRYPT, type = CommandType.BOOLEAN, description = "listed offerings support disk encryption", since = "4.18") + @Parameter(name = ApiConstants.ENCRYPT, type = CommandType.BOOLEAN, description = "Listed offerings support disk encryption", since = "4.18") private Boolean encrypt; @Parameter(name = ApiConstants.STORAGE_TYPE, 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 3b693fe57b7e..cd6baaf827bb 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 @@ -45,51 +45,51 @@ public class ListServiceOfferingsCmd extends BaseListProjectAndAccountResourcesC @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, description = "ID of the service offering") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "name of the service offering") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Name of the service offering") private String serviceOfferingName; @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, - description = "the ID of the virtual machine. Pass this in if you want to see the available service offering that a virtual machine can be changed to.") + description = "The ID of the Instance. Pass this in if you want to see the available service offering that an Instance can be changed to.") private Long virtualMachineId; - @Parameter(name=ApiConstants.IS_SYSTEM_OFFERING, type=CommandType.BOOLEAN, description="is this a system vm offering") + @Parameter(name=ApiConstants.IS_SYSTEM_OFFERING, type=CommandType.BOOLEAN, description = " Is this a System VM offering") private Boolean isSystem; @Parameter(name = ApiConstants.SYSTEM_VM_TYPE, type = CommandType.STRING, - description = "the system VM type. Possible types are \"consoleproxy\", \"secondarystoragevm\" or \"domainrouter\".") + description = "The system Instance type. Possible types are \"consoleproxy\", \"secondarystoragevm\" or \"domainrouter\".") private String systemVmType; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, - description = "id of zone disk offering is associated with", + description = "ID of zone disk offering is associated with", since = "4.13") private Long zoneId; @Parameter(name = ApiConstants.CPU_NUMBER, type = CommandType.INTEGER, - description = "the CPU number that listed offerings must support", + description = "The CPU number that listed offerings must support", since = "4.15") private Integer cpuNumber; @Parameter(name = ApiConstants.MEMORY, type = CommandType.INTEGER, - description = "the RAM memory that listed offering must support", + description = "The RAM memory that listed offering must support", since = "4.15") private Integer memory; @Parameter(name = ApiConstants.CPU_SPEED, type = CommandType.INTEGER, - description = "the CPU speed that listed offerings must support", + description = "The CPU speed that listed offerings must support", since = "4.15") private Integer cpuSpeed; @Parameter(name = ApiConstants.ENCRYPT_ROOT, type = CommandType.BOOLEAN, - description = "listed offerings support root disk encryption", + description = "Listed offerings support root disk encryption", since = "4.18") private Boolean encryptRoot; @@ -193,6 +193,22 @@ public Boolean getGpuEnabled() { return gpuEnabled; } + public void setZoneId(Long zoneId) { + this.zoneId = zoneId; + } + + public void setCpuNumber(Integer cpuNumber) { + this.cpuNumber = cpuNumber; + } + + public void setMemory(Integer memory) { + this.memory = memory; + } + + public void setEncryptRoot(Boolean encryptRoot) { + this.encryptRoot = encryptRoot; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/project/ActivateProjectCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/project/ActivateProjectCmd.java index 42e045d4389f..c6717ac659a4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/project/ActivateProjectCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/project/ActivateProjectCmd.java @@ -42,7 +42,7 @@ public class ActivateProjectCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ProjectResponse.class, required = true, description = "id of the project to be modified") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ProjectResponse.class, required = true, description = "ID of the project to be modified") private Long id; ///////////////////////////////////////////////////// @@ -80,7 +80,7 @@ public List getEntityOwnerIds() { @Override public void execute() { - CallContext.current().setEventDetails("Project id: " + getId()); + CallContext.current().setEventDetails("Project ID: " + getResourceUuid(ApiConstants.ID)); Project project = _projectService.activateProject(getId()); if (project != null) { ProjectResponse response = _responseGenerator.createProjectResponse(project); @@ -98,6 +98,6 @@ public String getEventType() { @Override public String getEventDescription() { - return "Activating project: " + id; + return "Activating project with ID: " + getResourceUuid(ApiConstants.ID); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/project/CreateProjectCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/project/CreateProjectCmd.java index cb93729381a4..2d0e92d47d4f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/project/CreateProjectCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/project/CreateProjectCmd.java @@ -44,20 +44,20 @@ public class CreateProjectCmd extends BaseAsyncCreateCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "account who will be Admin for the project") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "Account who will be Admin for the project") private String accountName; @Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class, - description = "user ID of the account to be assigned as owner of the project i.e., Project Admin", since = "4.15.0") + description = "User ID of the Account to be assigned as owner of the project i.e., Project Admin", since = "4.15.0") private Long userId; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "domain ID of the account owning a project") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "Domain ID of the Account owning a project") private Long domainId; - @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "ID of the account owning a project") + @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "ID of the Account owning a project") private Long accountId; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "name of the project") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the project") private String name; @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "The display text of the project, defaults to the 'name´.") diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/project/DeleteProjectCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/project/DeleteProjectCmd.java index 1fd205fdae41..c2a0c132448b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/project/DeleteProjectCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/project/DeleteProjectCmd.java @@ -42,10 +42,10 @@ public class DeleteProjectCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ProjectResponse.class, required = true, description = "id of the project to be deleted") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ProjectResponse.class, required = true, description = "ID of the project to be deleted") private Long id; - @Parameter(name = ApiConstants.CLEANUP, type = CommandType.BOOLEAN, since = "4.16.0", description = "true if all project resources have to be cleaned up, false otherwise") + @Parameter(name = ApiConstants.CLEANUP, type = CommandType.BOOLEAN, since = "4.16.0", description = "True if all project resources have to be cleaned up, false otherwise") private Boolean cleanup; ///////////////////////////////////////////////////// @@ -66,7 +66,7 @@ public Boolean isCleanup() { @Override public void execute() { - CallContext.current().setEventDetails("Project Id: " + id); + CallContext.current().setEventDetails("Project ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _projectService.deleteProject(id, isCleanup()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); @@ -83,7 +83,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Deleting project: " + id; + return "Deleting project with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/project/DeleteProjectInvitationCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/project/DeleteProjectInvitationCmd.java index d1b17eda76b1..b1d129b8af77 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/project/DeleteProjectInvitationCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/project/DeleteProjectInvitationCmd.java @@ -37,7 +37,7 @@ public class DeleteProjectInvitationCmd extends BaseAsyncCmd { // /////////////////////////////////////////////////// // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ProjectInvitationResponse.class, required = true, description = "id of the invitation") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ProjectInvitationResponse.class, required = true, description = "ID of the invitation") private Long id; // /////////////////////////////////////////////////// @@ -59,7 +59,7 @@ public long getEntityOwnerId() { @Override public void execute() { - CallContext.current().setEventDetails("Project invitation id " + id); + CallContext.current().setEventDetails("Project invitation ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _projectService.deleteProjectInvitation(id); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); @@ -76,7 +76,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Project invitatino id " + id + " is being removed"; + return "Removing project invitation with ID: " + getResourceUuid(ApiConstants.ID); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/project/ListProjectInvitationsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/project/ListProjectInvitationsCmd.java index 210394ec2ddf..679ba863b10a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/project/ListProjectInvitationsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/project/ListProjectInvitationsCmd.java @@ -37,21 +37,21 @@ public class ListProjectInvitationsCmd extends BaseListAccountResourcesCmd { // /////////////////////////////////////////////////// // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "list by project id") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "List by project ID") private Long projectId; @Parameter(name = ApiConstants.ACTIVE_ONLY, type = CommandType.BOOLEAN, - description = "if true, list only active invitations - having Pending state and ones that are not timed out yet") + description = "If true, list only active invitations - having Pending state and ones that are not timed out yet") private boolean activeOnly; - @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "list invitations by state") + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "List invitations by state") private String state; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ProjectInvitationResponse.class, description = "list invitations by id") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ProjectInvitationResponse.class, description = "List invitations by ID") private Long id; - @Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class, description = "list invitation by user ID") + @Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class, description = "List invitation by user ID") private Long userId; // /////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/project/ListProjectsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/project/ListProjectsCmd.java index d4679dbe0578..2f65429aca6a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/project/ListProjectsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/project/ListProjectsCmd.java @@ -48,16 +48,16 @@ public class ListProjectsCmd extends BaseListAccountResourcesCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "list projects by project ID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "List projects by project ID") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "list projects by name") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "List projects by name") private String name; - @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "list projects by display text") + @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "List projects by display text") private String displayText; - @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "list projects by state") + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "List projects by state") private String state; @Parameter(name = ApiConstants.TAGS, type = CommandType.MAP, description = "List projects by tags (key/value pairs)") @@ -69,11 +69,11 @@ public class ListProjectsCmd extends BaseListAccountResourcesCmd { @Parameter(name = ApiConstants.DETAILS, type = CommandType.LIST, collectionType = CommandType.STRING, - description = "comma separated list of project details requested, value can be a list of [ all, resource, min]") + description = "Comma separated list of project details requested, value can be a list of [ all, resource, min]") private List viewDetails; @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, - description = "flag to display the resource icon for projects") + description = "Flag to display the resource icon for projects") private Boolean showIcon; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/project/SuspendProjectCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/project/SuspendProjectCmd.java index a3eee8c80bb7..f67d0d55587d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/project/SuspendProjectCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/project/SuspendProjectCmd.java @@ -43,7 +43,7 @@ public class SuspendProjectCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ProjectResponse.class, required = true, description = "id of the project to be suspended") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ProjectResponse.class, required = true, description = "ID of the project to be suspended") private Long id; ///////////////////////////////////////////////////// @@ -60,7 +60,7 @@ public Long geId() { @Override public void execute() throws ConcurrentOperationException, ResourceUnavailableException { - CallContext.current().setEventDetails("Project Id: " + id); + CallContext.current().setEventDetails("Project ID: " + getResourceUuid(ApiConstants.ID)); Project project = _projectService.suspendProject(id); if (project != null) { ProjectResponse response = _responseGenerator.createProjectResponse(project); @@ -78,7 +78,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Suspending project: " + id; + return "Suspending project with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/project/UpdateProjectCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/project/UpdateProjectCmd.java index 4fabf7da788c..2d9bb92e4a36 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/project/UpdateProjectCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/project/UpdateProjectCmd.java @@ -45,27 +45,27 @@ public class UpdateProjectCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ProjectResponse.class, required = true, description = "id of the project to be modified") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ProjectResponse.class, required = true, description = "ID of the project to be modified") private Long id; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "new Admin account for the project") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "New Admin Account for the project") private String accountName; - @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "display text of the project") + @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "Display text of the project") private String displayText; - @Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class, description = "ID of the user to be promoted/demoted") + @Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class, description = "ID of the User to be promoted/demoted") private Long userId; - @Parameter(name = ApiConstants.ROLE_TYPE, type = CommandType.STRING, description = "Account level role to be assigned to the user/account : Admin/Regular") + @Parameter(name = ApiConstants.ROLE_TYPE, type = CommandType.STRING, description = "Account level role to be assigned to the User/Account : Admin/Regular") private String roleType; - @Parameter(name = ApiConstants.SWAP_OWNER, type = CommandType.BOOLEAN, description = "when true, it swaps ownership with the account/ user provided. " + + @Parameter(name = ApiConstants.SWAP_OWNER, type = CommandType.BOOLEAN, description = "When true, it swaps ownership with the Account/User provided. " + "Ideally to be used when a single project administrator is present. In case of multiple project admins, swapowner is to be set to false," + "to promote or demote the user/account based on the roleType (Regular or Admin) provided. Defaults to true") private Boolean swapOwner; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "name of the project", since = "4.19.0") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Name of the project", since = "4.19.0") private String name; ///////////////////////////////////////////////////// @@ -133,10 +133,10 @@ public List getEntityOwnerIds() { @Override public void execute() throws ResourceAllocationException { - CallContext.current().setEventDetails("Project id: " + getId()); + CallContext.current().setEventDetails("Project ID: " + getResourceUuid(ApiConstants.ID)); if (getAccountName() != null && getUserId() != null) { - throw new InvalidParameterValueException("Account name and user ID are mutually exclusive. Provide either account name" + - "to update account or user ID to update the user of the project"); + throw new InvalidParameterValueException("Account name and User ID are mutually exclusive. Provide either Account name" + + "to update Account or user ID to update the user of the project"); } Project project = null; @@ -161,6 +161,6 @@ public String getEventType() { @Override public String getEventDescription() { - return "Updating project: " + id; + return "Updating project with ID: " + getResourceUuid(ApiConstants.ID); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/project/UpdateProjectInvitationCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/project/UpdateProjectInvitationCmd.java index 0cbd9f702c27..34918de7339f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/project/UpdateProjectInvitationCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/project/UpdateProjectInvitationCmd.java @@ -38,10 +38,10 @@ public class UpdateProjectInvitationCmd extends BaseAsyncCmd { // /////////////////////////////////////////////////// // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, required = true, description = "id of the project to join") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, required = true, description = "ID of the project to join") private Long projectId; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "account that is joining the project") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "Account that is joining the project") private String accountName; @Parameter(name = ApiConstants.USER_ID, type = BaseCmd.CommandType.UUID, entityType = UserResponse.class, @@ -50,10 +50,10 @@ public class UpdateProjectInvitationCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.TOKEN, type = CommandType.STRING, - description = "list invitations for specified account; this parameter has to be specified with domainId") + description = "List invitations for specified account; this parameter has to be specified with domainId") private String token; - @Parameter(name = ApiConstants.ACCEPT, type = CommandType.BOOLEAN, description = "if true, accept the invitation, decline if false. True by default") + @Parameter(name = ApiConstants.ACCEPT, type = CommandType.BOOLEAN, description = "If true, accept the invitation, decline if false. True by default") private Boolean accept; // /////////////////////////////////////////////////// @@ -92,7 +92,7 @@ public long getEntityOwnerId() { @Override public void execute() { - String eventDetails = "Project id: " + projectId + ";"; + String eventDetails = "Project id: " + getResourceUuid(ApiConstants.PROJECT_ID) + ";"; if (accountName != null) { eventDetails += " accountName: " + accountName + ";"; } else if (userId != null) { @@ -116,6 +116,6 @@ public String getEventType() { @Override public String getEventDescription() { - return "Updating project invitation for projectId " + projectId; + return "Updating project invitation for project with ID: " + getResourceUuid(ApiConstants.PROJECT_ID); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/AssignToGlobalLoadBalancerRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/AssignToGlobalLoadBalancerRuleCmd.java index 649b2a7bd9bc..8bb38d97c134 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/AssignToGlobalLoadBalancerRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/AssignToGlobalLoadBalancerRuleCmd.java @@ -62,7 +62,7 @@ public class AssignToGlobalLoadBalancerRuleCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = GlobalLoadBalancerResponse.class, required = true, - description = "the ID of the global load balancer rule") + description = "The ID of the global load balancer rule") private Long id; @Parameter(name = ApiConstants.LOAD_BALANCER_RULE_LIST, @@ -70,7 +70,7 @@ public class AssignToGlobalLoadBalancerRuleCmd extends BaseAsyncCmd { collectionType = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, - description = "the list load balancer rules that will be assigned to global load balancer rule") + description = "The list load balancer rules that will be assigned to global load balancer rule") private List loadBalancerRulesIds; @Parameter(name = ApiConstants.GSLB_LBRULE_WEIGHT_MAP, @@ -145,13 +145,13 @@ public String getEventType() { @Override public String getEventDescription() { - return "assign load balancer rules " + StringUtils.join(getLoadBalancerRulesIds(), ",") + " to global load balancer rule " + getGlobalLoadBalancerRuleId(); + return "Assigning load balancer rules " + StringUtils.join(getLoadBalancerRulesIds(), ",") + " to global load balancer rule " + getResourceUuid(ApiConstants.ID); } @Override public void execute() { CallContext.current().setEventDetails( - "Global Load balancer rule Id: " + getGlobalLoadBalancerRuleId() + " VmIds: " + StringUtils.join(getLoadBalancerRulesIds(), ",")); + "Global Load balancer rule ID: " + getResourceUuid(ApiConstants.ID) + " Instances IDs: " + StringUtils.join(getLoadBalancerRulesIds(), ",")); boolean result = _gslbService.assignToGlobalLoadBalancerRule(this); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/CreateGlobalLoadBalancerRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/CreateGlobalLoadBalancerRuleCmd.java index ddaadde78524..2ecd8ef22e65 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/CreateGlobalLoadBalancerRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/CreateGlobalLoadBalancerRuleCmd.java @@ -47,41 +47,41 @@ public class CreateGlobalLoadBalancerRuleCmd extends BaseAsyncCreateCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "name of the load balancer rule") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the load balancer rule") private String globalLoadBalancerRuleName; - @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "the description of the load balancer rule", length = 4096) + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "The description of the load balancer rule", length = 4096) private String description; @Parameter(name = ApiConstants.REGION_ID, type = CommandType.INTEGER, entityType = RegionResponse.class, required = true, - description = "region where the global load balancer is going to be created.") + description = "Region where the global load balancer is going to be created.") private Integer regionId; @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, - description = "the account associated with the global load balancer. Must be used with the domainId parameter.") + description = "The Account associated with the global load balancer. Must be used with the domainId parameter.") private String accountName; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "the domain ID associated with the load balancer") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "The domain ID associated with the load balancer") private Long domainId; @Parameter(name = ApiConstants.GSLB_LB_METHOD, type = CommandType.STRING, required = false, - description = "load balancer algorithm (roundrobin, leastconn, proximity) " + description = "Load balancer algorithm (roundrobin, leastconn, proximity) " + "that method is used to distribute traffic across the zones participating in global server load balancing, if not specified defaults to 'round robin'") private String algorithm; @Parameter(name = ApiConstants.GSLB_STICKY_SESSION_METHOD, type = CommandType.STRING, required = false, - description = "session sticky method (sourceip) if not specified defaults to sourceip") + description = "Session sticky method (sourceip) if not specified defaults to sourceip") private String stickyMethod; - @Parameter(name = ApiConstants.GSLB_SERVICE_DOMAIN_NAME, type = CommandType.STRING, required = true, description = "domain name for the GSLB service.") + @Parameter(name = ApiConstants.GSLB_SERVICE_DOMAIN_NAME, type = CommandType.STRING, required = true, description = "Domain name for the GSLB service.") private String serviceDomainName; @Parameter(name = ApiConstants.GSLB_SERVICE_TYPE, type = CommandType.STRING, required = true, description = "GSLB service type (tcp, udp, http)") @@ -153,7 +153,7 @@ public void create() { GlobalLoadBalancerRule gslbRule = _gslbService.createGlobalLoadBalancerRule(this); this.setEntityId(gslbRule.getId()); this.setEntityUuid(gslbRule.getUuid()); - CallContext.current().setEventDetails("Rule Id: " + getEntityId()); + CallContext.current().setEventDetails("Rule ID: " + getEntityUuid()); } catch (Exception ex) { logger.warn("Exception: ", ex); throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); @@ -180,7 +180,7 @@ public ApiCommandResourceType getApiResourceType() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, null, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, null, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/DeleteGlobalLoadBalancerRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/DeleteGlobalLoadBalancerRuleCmd.java index 7f3308614ccf..b44b547463e5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/DeleteGlobalLoadBalancerRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/DeleteGlobalLoadBalancerRuleCmd.java @@ -50,7 +50,7 @@ public class DeleteGlobalLoadBalancerRuleCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = GlobalLoadBalancerResponse.class, required = true, - description = "the ID of the global load balancer rule") + description = "The ID of the global load balancer rule") private Long id; ///////////////////////////////////////////////////// @@ -85,12 +85,12 @@ public String getEventType() { @Override public String getEventDescription() { - return "deleting global load balancer rule: " + getGlobalLoadBalancerId(); + return "Deleting global load balancer rule with ID: " + getResourceUuid(ApiConstants.ID); } @Override public void execute() { - CallContext.current().setEventDetails("Deleting global Load balancer rule Id: " + getGlobalLoadBalancerId()); + CallContext.current().setEventDetails("Deleting global Load balancer rule with ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _gslbService.deleteGlobalLoadBalancerRule(this); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/ListGlobalLoadBalancerRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/ListGlobalLoadBalancerRuleCmd.java index bf0cf22a2ecb..a4bd027fc976 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/ListGlobalLoadBalancerRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/ListGlobalLoadBalancerRuleCmd.java @@ -43,10 +43,10 @@ public class ListGlobalLoadBalancerRuleCmd extends BaseListTaggedResourcesCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GlobalLoadBalancerResponse.class, description = "the ID of the global load balancer rule") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GlobalLoadBalancerResponse.class, description = "The ID of the global load balancer rule") private Long id; - @Parameter(name = ApiConstants.REGION_ID, type = CommandType.INTEGER, entityType = RegionResponse.class, description = "region ID") + @Parameter(name = ApiConstants.REGION_ID, type = CommandType.INTEGER, entityType = RegionResponse.class, description = "Region ID") private Integer regionId; // /////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/RemoveFromGlobalLoadBalancerRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/RemoveFromGlobalLoadBalancerRuleCmd.java index d4b02139892c..a0ec9a1296ab 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/RemoveFromGlobalLoadBalancerRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/RemoveFromGlobalLoadBalancerRuleCmd.java @@ -65,7 +65,7 @@ public class RemoveFromGlobalLoadBalancerRuleCmd extends BaseAsyncCmd { collectionType = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, - description = "the list load balancer rules that will be assigned to global load balancer rule") + description = "The list load balancer rules that will be assigned to global load balancer rule") private List loadBalancerRulesIds; ///////////////////////////////////////////////////// @@ -108,13 +108,13 @@ public String getEventType() { @Override public String getEventDescription() { - return "removing load balancer rules:" + StringUtils.join(getLoadBalancerRulesIds(), ",") + " from global load balancer: " + getGlobalLoadBalancerRuleId(); + return "Removing load balancer rules:" + StringUtils.join(getLoadBalancerRulesIds(), ",") + " from global load balancer: " + getResourceUuid(ApiConstants.ID); } @Override public void execute() { CallContext.current().setEventDetails( - "Global Load balancer rule Id: " + getGlobalLoadBalancerRuleId() + " VmIds: " + StringUtils.join(getLoadBalancerRulesIds(), ",")); + "Global Load balancer rule Id: " + getResourceUuid(ApiConstants.ID) + " VmIds: " + StringUtils.join(getLoadBalancerRulesIds(), ",")); boolean result = _gslbService.removeFromGlobalLoadBalancerRule(this); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/UpdateGlobalLoadBalancerRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/UpdateGlobalLoadBalancerRuleCmd.java index 7996998e5d92..a56672e29cac 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/UpdateGlobalLoadBalancerRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/region/ha/gslb/UpdateGlobalLoadBalancerRuleCmd.java @@ -33,7 +33,7 @@ import com.cloud.region.ha.GlobalLoadBalancingRulesService; import com.cloud.user.Account; -@APICommand(name = "updateGlobalLoadBalancerRule", description = "update global load balancer rules.", responseObject = GlobalLoadBalancerResponse.class, +@APICommand(name = "updateGlobalLoadBalancerRule", description = "Update global load balancer rules.", responseObject = GlobalLoadBalancerResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class UpdateGlobalLoadBalancerRuleCmd extends BaseAsyncCmd { @@ -46,23 +46,23 @@ public class UpdateGlobalLoadBalancerRuleCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = GlobalLoadBalancerResponse.class, required = true, - description = "the ID of the global load balancer rule") + description = "The ID of the global load balancer rule") private Long id; - @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "the description of the load balancer rule", length = 4096) + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "The description of the load balancer rule", length = 4096) private String description; @Parameter(name = ApiConstants.GSLB_LB_METHOD, type = CommandType.STRING, required = false, - description = "load balancer algorithm (roundrobin, leastconn, proximity) " + description = "Load balancer algorithm (roundrobin, leastconn, proximity) " + "that is used to distributed traffic across the zones participating in global server load balancing, if not specified defaults to 'round robin'") private String algorithm; @Parameter(name = ApiConstants.GSLB_STICKY_SESSION_METHOD, type = CommandType.STRING, required = false, - description = "session sticky method (sourceip) if not specified defaults to sourceip") + description = "Session sticky method (sourceip) if not specified defaults to sourceip") private String stickyMethod; // /////////////////////////////////////////////////// @@ -107,7 +107,7 @@ public long getEntityOwnerId() { @Override public void execute() { - org.apache.cloudstack.context.CallContext.current().setEventDetails("Global Load balancer Id: " + getId()); + org.apache.cloudstack.context.CallContext.current().setEventDetails("Global Load balancer ID: " + getResourceUuid(ApiConstants.ID)); GlobalLoadBalancerRule gslbRule = _gslbService.updateGlobalLoadBalancerRule(this); if (gslbRule != null) { GlobalLoadBalancerResponse response = _responseGenerator.createGlobalLoadBalancerResponse(gslbRule); @@ -125,6 +125,6 @@ public String getEventType() { @Override public String getEventDescription() { - return "updating global load balancer rule"; + return "Updating global load balancer rule with ID: " + getResourceUuid(ApiConstants.ID); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/resource/GetCloudIdentifierCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/resource/GetCloudIdentifierCmd.java index b9e43336217d..04b0ccc4cb07 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/resource/GetCloudIdentifierCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/resource/GetCloudIdentifierCmd.java @@ -42,7 +42,7 @@ public class GetCloudIdentifierCmd extends BaseCmd { type = CommandType.UUID, entityType = UserResponse.class, required = true, - description = "the user ID for the cloud identifier") + description = "The user ID for the cloud identifier") private Long userid; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/resource/ListDetailOptionsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/resource/ListDetailOptionsCmd.java index 240852a74c52..273f931fa23c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/resource/ListDetailOptionsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/resource/ListDetailOptionsCmd.java @@ -28,7 +28,7 @@ import org.apache.commons.lang3.StringUtils; @APICommand(name = "listDetailOptions", - description = "Lists all possible details and their options for a resource type such as a VM or a template", + description = "Lists all possible details and their options for a resource type such as an Instance or a Template", responseObject = DetailOptionsResponse.class, since = "4.13", requestHasSensitiveInfo = false, @@ -41,11 +41,11 @@ public class ListDetailOptionsCmd extends BaseCmd { ///////////////////////////////////////////////////// @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, required = true, - description = "the resource type such as UserVm, Template etc.") + description = "The resource type such as UserVm, Template etc.") private String resourceType; @Parameter(name = ApiConstants.RESOURCE_ID, type = CommandType.STRING, - description = "the UUID of the resource (optional)") + description = "The UUID of the resource (optional)") private String resourceId; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/resource/ListHypervisorsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/resource/ListHypervisorsCmd.java index 556f3b081f02..70d339abd6f2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/resource/ListHypervisorsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/resource/ListHypervisorsCmd.java @@ -38,7 +38,7 @@ public class ListHypervisorsCmd extends BaseCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the zone id for listing hypervisors.") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The zone id for listing hypervisors.") private Long zoneId; // /////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/resource/ListResourceLimitsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/resource/ListResourceLimitsCmd.java index 71b886ed12b6..4bda23617092 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/resource/ListResourceLimitsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/resource/ListResourceLimitsCmd.java @@ -43,11 +43,11 @@ public class ListResourceLimitsCmd extends BaseListProjectAndAccountResourcesCmd private Long id; @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.INTEGER, description = "Type of resource. Values are 0, 1, 2, 3, 4, 6, 7, 8, 9, 10 and 11. " - + "0 - Instance. Number of instances a user can create. " + + "0 - Instance. Number of Instances a user can create. " + "1 - IP. Number of public IP addresses an account can own. " + "2 - Volume. Number of disk volumes an account can own. " - + "3 - Snapshot. Number of snapshots an account can own. " - + "4 - Template. Number of templates an account can register/create. " + + "3 - Snapshot. Number of Snapshots an account can own. " + + "4 - Template. Number of Templates an account can register/create. " + "5 - Project. Number of projects an account can own. " + "6 - Network. Number of networks an account can own. " + "7 - VPC. Number of VPC an account can own. " @@ -58,11 +58,11 @@ public class ListResourceLimitsCmd extends BaseListProjectAndAccountResourcesCmd private Integer resourceType; @Parameter(name = ApiConstants.RESOURCE_TYPE_NAME, type = CommandType.STRING, description = "Type of resource (wins over resourceType if both are provided). Values are: " - + "user_vm - Instance. Number of instances a user can create. " + + "user_vm - Instance. Number of Instances a user can create. " + "public_ip - IP. Number of public IP addresses an account can own. " + "volume - Volume. Number of disk volumes an account can own. " - + "snapshot - Snapshot. Number of snapshots an account can own. " - + "template - Template. Number of templates an account can register/create. " + + "snapshot - Snapshot. Number of Snapshots an account can own. " + + "template - Template. Number of Templates an account can register/create. " + "project - Project. Number of projects an account can own. " + "network - Network. Number of networks an account can own. " + "vpc - VPC. Number of VPC an account can own. " @@ -103,7 +103,7 @@ public String getTag() { @Override public void execute() { List result = - _resourceLimitService.searchForLimits(id, _accountService.finalyzeAccountId(this.getAccountName(), this.getDomainId(), this.getProjectId(), false), this.getDomainId(), + _resourceLimitService.searchForLimits(id, _accountService.finalizeAccountId(this.getAccountName(), this.getDomainId(), this.getProjectId(), false), this.getDomainId(), getResourceTypeEnum(), getTag(), this.getStartIndex(), this.getPageSizeVal()); ListResponse response = new ListResponse(); List limitResponses = new ArrayList(); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmd.java index 49c6ee605c8c..d43bb29e9d27 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmd.java @@ -61,11 +61,11 @@ public class UpdateResourceCountCmd extends BaseCmd { @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.INTEGER, description = "Type of resource to update. If specifies valid values are 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 and 11. If not specified will update all resource counts" - + "0 - Instance. Number of instances a user can create. " + + "0 - Instance. Number of Instances a user can create. " + "1 - IP. Number of public IP addresses a user can own. " + "2 - Volume. Number of disk volumes a user can create. " - + "3 - Snapshot. Number of snapshots a user can create. " - + "4 - Template. Number of templates that a user can register/create. " + + "3 - Snapshot. Number of Snapshots a user can create. " + + "4 - Template. Number of Templates that a user can register/create. " + "5 - Project. Number of projects that a user can create. " + "6 - Network. Number of guest network a user can create. " + "7 - VPC. Number of VPC a user can create. " @@ -127,7 +127,7 @@ public long getEntityOwnerId() { @Override public void execute() { List result = - _resourceLimitService.recalculateResourceCount(_accountService.finalyzeAccountId(accountName, domainId, projectId, true), getDomainId(), getResourceType(), getTag()); + _resourceLimitService.recalculateResourceCount(_accountService.finalizeAccountId(accountName, domainId, projectId, true), getDomainId(), getResourceType(), getTag()); if ((result != null) && (result.size() > 0)) { ListResponse response = new ListResponse(); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/resource/UpdateResourceLimitCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/resource/UpdateResourceLimitCmd.java index 3538a389a6e9..f88ef9678e31 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/resource/UpdateResourceLimitCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/resource/UpdateResourceLimitCmd.java @@ -57,13 +57,13 @@ public class UpdateResourceLimitCmd extends BaseCmd { type = CommandType.INTEGER, required = true, description = "Type of resource to update. Values are 0, 1, 2, 3, 4, 6, 7, 8, 9, 10 and 11. " - + "0 - Instance. Number of instances a user can create. " + + "0 - Instance. Number of Instances a user can create. " + "1 - IP. Number of public IP addresses a user can own. " + "2 - Volume. Number of disk volumes a user can create. " - + "3 - Snapshot. Number of snapshots a user can create. " - + "4 - Template. Number of templates that a user can register/create. " - + "5 - Project. Number of projects a user can create. " - + "6 - Network. Number of guest network a user can create. " + + "3 - Snapshot. Number of Snapshots a user can create. " + + "4 - Template. Number of Templates that a user can register/create. " + + "5 - Project. Number of Projects a user can create. " + + "6 - Network. Number of guest Network a user can create. " + "7 - VPC. Number of VPC a user can create. " + "8 - CPU. Total number of CPU cores a user can use. " + "9 - Memory. Total Memory (in MB) a user can use. " @@ -100,7 +100,7 @@ public Integer getResourceType() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } @@ -110,7 +110,7 @@ public long getEntityOwnerId() { @Override public void execute() { - ResourceLimit result = _resourceLimitService.updateResourceLimit(_accountService.finalyzeAccountId(accountName, domainId, projectId, true), getDomainId(), resourceType, max, getTag()); + ResourceLimit result = _resourceLimitService.updateResourceLimit(_accountService.finalizeAccountId(accountName, domainId, projectId, true), getDomainId(), resourceType, max, getTag()); if (result != null || (result == null && max != null && max.longValue() == -1L)) { ResourceLimitResponse response = _responseGenerator.createResourceLimitResponse(result); response.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/schedule/CreateResourceScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/schedule/CreateResourceScheduleCmd.java new file mode 100644 index 000000000000..f6268106c317 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/schedule/CreateResourceScheduleCmd.java @@ -0,0 +1,132 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.schedule; + +import com.cloud.exception.InvalidParameterValueException; +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.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.ResourceScheduleResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.schedule.ResourceScheduleManager; +import org.apache.commons.lang3.EnumUtils; + +import javax.inject.Inject; +import java.util.Date; +import java.util.Map; + +@APICommand(name = "createResourceSchedule", description = "Create Resource Schedule", responseObject = ResourceScheduleResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class CreateResourceScheduleCmd extends BaseCmd { + + @Inject + ResourceScheduleManager resourceScheduleManager; + + @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, required = true, description = "Type of the resource") + private String resourceType; + + @Parameter(name = ApiConstants.RESOURCE_ID, type = CommandType.STRING, required = true, description = "ID of the resource for which schedule is to be defined") + private String resourceId; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, required = false, description = "Description of the schedule") + private String description; + + @Parameter(name = ApiConstants.SCHEDULE, type = CommandType.STRING, required = true, description = "Schedule for action on resource in cron format. e.g. '0 15 10 * *' for 'at 15:00 on 10th day of every month'") + private String schedule; + + @Parameter(name = ApiConstants.TIMEZONE, type = CommandType.STRING, required = true, description = "Specifies a timezone for this command. For more information on the timezone parameter, see TimeZone Format.") + private String timeZone; + + @Parameter(name = ApiConstants.ACTION, type = CommandType.STRING, required = true, description = "Action to take on the resource.") + private String action; + + @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, required = false, description = "Start date from which the schedule becomes active. Defaults to current date plus 1 minute. (Format \"yyyy-MM-dd hh:mm:ss\")") + private Date startDate; + + @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, required = false, description = "End date after which the schedule becomes inactive. (Format \"yyyy-MM-dd hh:mm:ss\")") + private Date endDate; + + @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, required = false, description = "Enable schedule. Defaults to true") + private Boolean enabled; + + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, required = false, description = "Map of (key/value pairs) details for the schedule.") + private Map details; + + public ApiCommandResourceType getResourceType() { + ApiCommandResourceType type = EnumUtils.getEnumIgnoreCase(ApiCommandResourceType.class, resourceType); + if (type == null) { + throw new InvalidParameterValueException("Unknown resource type: " + resourceType); + } + return type; + } + + public String getResourceId() { + return resourceId; + } + + public String getDescription() { + return description; + } + + public String getSchedule() { + return schedule; + } + + public String getTimeZone() { + return timeZone; + } + + public String getAction() { + return action; + } + + public Date getStartDate() { + return startDate; + } + + public Date getEndDate() { + return endDate; + } + + public Boolean getEnabled() { + if (enabled == null) { + enabled = true; + } + return enabled; + } + + public Map getDetails() { + return convertDetailsToMap(details); + } + + @Override + public void execute() { + ResourceScheduleResponse response = resourceScheduleManager.createSchedule(getResourceType(), getResourceId(), + getDescription(), getSchedule(), getTimeZone(), getAction(), getStartDate(), getEndDate(), getEnabled(), getDetails()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getAccountId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/schedule/DeleteResourceScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/schedule/DeleteResourceScheduleCmd.java new file mode 100644 index 000000000000..fe9a695df637 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/schedule/DeleteResourceScheduleCmd.java @@ -0,0 +1,86 @@ +// 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.schedule; + +import com.cloud.exception.InvalidParameterValueException; +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.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.ResourceScheduleResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.schedule.ResourceScheduleManager; +import org.apache.commons.lang3.EnumUtils; + +import javax.inject.Inject; +import java.util.List; + +@APICommand(name = "deleteResourceSchedule", description = "Delete Resource Schedule", responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class DeleteResourceScheduleCmd extends BaseCmd { + + @Inject + ResourceScheduleManager resourceScheduleManager; + + @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, required = true, description = "Type of the resource") + private String resourceType; + + @Parameter(name = ApiConstants.RESOURCE_ID, type = CommandType.STRING, required = true, description = "ID of the resource for which schedules are to be deleted") + private String resourceId; + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ResourceScheduleResponse.class, required = false, description = "ID of the schedule to be deleted") + private Long id; + + @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = ResourceScheduleResponse.class, required = false, description = "comma separated list of schedule ids to be deleted") + private List ids; + + public ApiCommandResourceType getResourceType() { + ApiCommandResourceType type = EnumUtils.getEnumIgnoreCase(ApiCommandResourceType.class, resourceType); + if (type == null) { + throw new InvalidParameterValueException("Unknown resource type: " + resourceType); + } + return type; + } + + public String getResourceId() { + return resourceId; + } + + public Long getId() { + return id; + } + + public List getIds() { + return ids; + } + + @Override + public void execute() { + resourceScheduleManager.removeSchedule(getResourceType(), getResourceId(), getId(), getIds()); + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getAccountId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/schedule/ListResourceScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/schedule/ListResourceScheduleCmd.java new file mode 100644 index 000000000000..1c8869eff9c3 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/schedule/ListResourceScheduleCmd.java @@ -0,0 +1,97 @@ +// 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.schedule; + +import com.cloud.exception.InvalidParameterValueException; +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.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ResourceScheduleResponse; +import org.apache.cloudstack.schedule.ResourceScheduleManager; +import org.apache.commons.lang3.EnumUtils; + +import javax.inject.Inject; +import java.util.List; + +@APICommand(name = "listResourceSchedule", description = "List Resource Schedules", responseObject = ResourceScheduleResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class ListResourceScheduleCmd extends BaseListCmd { + + @Inject + ResourceScheduleManager resourceScheduleManager; + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ResourceScheduleResponse.class, required = false, description = "ID of the schedule") + private Long id; + + @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = ResourceScheduleResponse.class, required = false, description = "comma separated list of schedule ids") + private List ids; + + @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, required = true, description = "Type of the resource.") + private String resourceType; + + @Parameter(name = ApiConstants.RESOURCE_ID, type = CommandType.STRING, required = true, description = "ID of the resource for which schedules are to be listed.") + private String resourceId; + + @Parameter(name = ApiConstants.ACTION, type = CommandType.STRING, required = false, description = "Action to take on the resource.") + private String action; + + @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, required = false, description = "Filter by enabled status.") + private Boolean enabled; + + public Long getId() { + return id; + } + + public List getIds() { + return ids; + } + + public ApiCommandResourceType getResourceType() { + ApiCommandResourceType type = EnumUtils.getEnumIgnoreCase(ApiCommandResourceType.class, resourceType); + if (type == null) { + throw new InvalidParameterValueException("Unknown resource type: " + resourceType); + } + return type; + } + + public String getResourceId() { + return resourceId; + } + + public String getAction() { + return action; + } + + public Boolean getEnabled() { + return enabled; + } + + @Override + public void execute() { + ListResponse response = resourceScheduleManager.listSchedule( + getId(), getIds(), getResourceType(), getResourceId(), getAction(), getEnabled(), + getStartIndex(), getPageSizeVal() + ); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/schedule/UpdateResourceScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/schedule/UpdateResourceScheduleCmd.java new file mode 100644 index 000000000000..5422588853e9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/schedule/UpdateResourceScheduleCmd.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.user.schedule; + +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.ResourceScheduleResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.schedule.ResourceScheduleManager; + +import javax.inject.Inject; +import java.util.Date; +import java.util.Map; + +@APICommand(name = "updateResourceSchedule", description = "Update Resource Schedule", responseObject = ResourceScheduleResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.23.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class UpdateResourceScheduleCmd extends BaseCmd { + + @Inject + ResourceScheduleManager resourceScheduleManager; + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ResourceScheduleResponse.class, required = true, description = "ID of the schedule to be updated") + private Long id; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, required = false, description = "Description of the schedule") + private String description; + + @Parameter(name = ApiConstants.SCHEDULE, type = CommandType.STRING, required = false, description = "Schedule for action on resource in cron format.") + private String schedule; + + @Parameter(name = ApiConstants.TIMEZONE, type = CommandType.STRING, required = false, description = "Specifies a timezone for this command.") + private String timeZone; + + @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, required = false, description = "Start date from which the schedule becomes active.") + private Date startDate; + + @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, required = false, description = "End date after which the schedule becomes inactive.") + private Date endDate; + + @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, required = false, description = "Enable or disable the schedule.") + private Boolean enabled; + + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, required = false, description = "Map of (key/value pairs) details for the schedule.") + private Map details; + + public Long getId() { + return id; + } + + public String getDescription() { + return description; + } + + public String getSchedule() { + return schedule; + } + + public String getTimeZone() { + return timeZone; + } + + public Date getStartDate() { + return startDate; + } + + public Date getEndDate() { + return endDate; + } + + public Boolean getEnabled() { + return enabled; + } + + public Map getDetails() { + return convertDetailsToMap(details); + } + + @Override + public void execute() { + ResourceScheduleResponse response = resourceScheduleManager.updateSchedule(getId(), getDescription(), getSchedule(), + getTimeZone(), getStartDate(), getEndDate(), getEnabled(), getDetails()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getAccountId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/AuthorizeSecurityGroupEgressCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/AuthorizeSecurityGroupEgressCmd.java index 13faafe348c0..7d0004c8e5d5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/AuthorizeSecurityGroupEgressCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/AuthorizeSecurityGroupEgressCmd.java @@ -58,43 +58,43 @@ public class AuthorizeSecurityGroupEgressCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "TCP is default. UDP is the other supported protocol") private String protocol; - @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "start port for this egress rule") + @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "Start port for this egress rule") private Integer startPort; - @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "end port for this egress rule") + @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "End port for this egress rule") private Integer endPort; - @Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "type of the icmp message being sent") + @Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "Type of the ICMP message being sent") private Integer icmpType; - @Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "error code for this icmp message") + @Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "Error code for this icmp message") private Integer icmpCode; - @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the cidr list associated. Multiple entries must be separated by a single comma character (,).") + @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "The CIDR list associated. Multiple entries must be separated by a single comma character (,).") private List cidrList; - @Parameter(name = ApiConstants.USER_SECURITY_GROUP_LIST, type = CommandType.MAP, description = "user to security group mapping") + @Parameter(name = ApiConstants.USER_SECURITY_GROUP_LIST, type = CommandType.MAP, description = "User to security group mapping") private Map userSecurityGroupList; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, - description = "an optional domainId for the security group. If the account parameter is used, domainId must also be used.", + description = "An optional domainId for the security group. If the account parameter is used, domainId must also be used.", entityType = DomainResponse.class) private Long domainId; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the security group. Must be used with domainId.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "An optional account for the security group. Must be used with domainId.") private String accountName; - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, description = "an optional project of the security group", entityType = ProjectResponse.class) + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, description = "An optional project of the security group", entityType = ProjectResponse.class) private Long projectId; @ACL(accessType = AccessType.OperateEntry) - @Parameter(name=ApiConstants.SECURITY_GROUP_ID, type=CommandType.UUID, description="The ID of the security group. Mutually exclusive with securityGroupName parameter", entityType=SecurityGroupResponse.class) + @Parameter(name=ApiConstants.SECURITY_GROUP_ID, type=CommandType.UUID, description = "The ID of the security group. Mutually exclusive with securityGroupName parameter", entityType=SecurityGroupResponse.class) private Long securityGroupId; // This @ACL will not work, since we don't have a way to convert this parameter to the entity like securityGroupId. //@ACL(accessType = AccessType.OperateEntry) - @Parameter(name=ApiConstants.SECURITY_GROUP_NAME, type=CommandType.STRING, description="The name of the security group. Mutually exclusive with securityGroupId parameter") + @Parameter(name=ApiConstants.SECURITY_GROUP_NAME, type=CommandType.STRING, description = "The name of the security group. Mutually exclusive with securityGroupId parameter") private String securityGroupName; ///////////////////////////////////////////////////// @@ -166,7 +166,7 @@ public static String getResultObjectName() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/AuthorizeSecurityGroupIngressCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/AuthorizeSecurityGroupIngressCmd.java index 640870fc3de3..d7a95d8204e6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/AuthorizeSecurityGroupIngressCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/AuthorizeSecurityGroupIngressCmd.java @@ -55,46 +55,46 @@ public class AuthorizeSecurityGroupIngressCmd extends BaseAsyncCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "the protocol for the ACL rule. Valid values are TCP/UDP/ICMP/ALL or valid protocol number (see /etc/protocols). ALL is default.") + @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "The protocol for the ACL rule. Valid values are TCP/UDP/ICMP/ALL or valid protocol number (see /etc/protocols). ALL is default.") private String protocol; - @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "start port for this ingress rule") + @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "Start port for this ingress rule") private Integer startPort; - @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "end port for this ingress rule") + @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "End port for this ingress rule") private Integer endPort; - @Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "type of the icmp message being sent") + @Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "Type of the icmp message being sent") private Integer icmpType; - @Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "error code for this icmp message") + @Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "Error code for this icmp message") private Integer icmpCode; - @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the cidr list associated. Multiple entries must be separated by a single comma character (,).") + @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "The cidr list associated. Multiple entries must be separated by a single comma character (,).") private List cidrList; - @Parameter(name = ApiConstants.USER_SECURITY_GROUP_LIST, type = CommandType.MAP, description = "user to security group mapping") + @Parameter(name = ApiConstants.USER_SECURITY_GROUP_LIST, type = CommandType.MAP, description = "User to security group mapping") private Map userSecurityGroupList; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, - description = "an optional domainId for the security group. If the account parameter is used, domainId must also be used.", + description = "An optional domainId for the security group. If the account parameter is used, domainId must also be used.", entityType = DomainResponse.class) private Long domainId; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the security group. Must be used with domainId.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "An optional account for the security group. Must be used with domainId.") private String accountName; - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, description = "an optional project of the security group", entityType = ProjectResponse.class) + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, description = "An optional project of the security group", entityType = ProjectResponse.class) private Long projectId; @ACL(accessType = AccessType.OperateEntry) - @Parameter(name=ApiConstants.SECURITY_GROUP_ID, type=CommandType.UUID, description="The ID of the security group. Mutually exclusive with securityGroupName parameter", entityType=SecurityGroupResponse.class) + @Parameter(name=ApiConstants.SECURITY_GROUP_ID, type=CommandType.UUID, description = "The ID of the security group. Mutually exclusive with securityGroupName parameter", entityType=SecurityGroupResponse.class) private Long securityGroupId; // This @ACL will not work, since we don't have a way to convert this parameter to the entity like securityGroupId. //@ACL(accessType = AccessType.OperateEntry) - @Parameter(name=ApiConstants.SECURITY_GROUP_NAME, type=CommandType.STRING, description="The name of the security group. Mutually exclusive with securityGroupId parameter") + @Parameter(name=ApiConstants.SECURITY_GROUP_NAME, type=CommandType.STRING, description = "The name of the security group. Mutually exclusive with securityGroupId parameter") private String securityGroupName; ///////////////////////////////////////////////////// @@ -166,7 +166,7 @@ public static String getResultObjectName() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/CreateSecurityGroupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/CreateSecurityGroupCmd.java index 673eaaef33da..333dc9ff7482 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/CreateSecurityGroupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/CreateSecurityGroupCmd.java @@ -40,19 +40,19 @@ public class CreateSecurityGroupCmd extends BaseCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the security group. Must be used with domainId.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "An optional account for the security group. Must be used with domainId.") private String accountName; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, - description = "an optional domainId for the security group. If the account parameter is used, domainId must also be used.", + description = "An optional domainId for the security group. If the account parameter is used, domainId must also be used.", entityType = DomainResponse.class) private Long domainId; - @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "the description of the security group") + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "The description of the security group") private String description; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "name of the security group") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the security group") private String securityGroupName; @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, description = "Create security group for project", entityType = ProjectResponse.class) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/DeleteSecurityGroupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/DeleteSecurityGroupCmd.java index b2ea90792b8e..1882d80c1c1b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/DeleteSecurityGroupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/DeleteSecurityGroupCmd.java @@ -43,20 +43,20 @@ public class DeleteSecurityGroupCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "the account of the security group. Must be specified with domain ID") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "The account of the security group. Must be specified with domain ID") private String accountName; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, - description = "the domain ID of account owning the security group", + description = "The domain ID of account owning the security group", entityType = DomainResponse.class) private Long domainId; - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, description = "the project of the security group", entityType = ProjectResponse.class) + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, description = "The project of the security group", entityType = ProjectResponse.class) private Long projectId; @ACL(accessType = AccessType.OperateEntry) - @Parameter(name=ApiConstants.ID, type=CommandType.UUID, description="The ID of the security group. Mutually exclusive with name parameter", entityType=SecurityGroupResponse.class) + @Parameter(name=ApiConstants.ID, type=CommandType.UUID, description = "The ID of the security group. Mutually exclusive with name parameter", entityType=SecurityGroupResponse.class) private Long id; @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The ID of the security group. Mutually exclusive with id parameter") @@ -103,7 +103,7 @@ public Long getId() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/ListSecurityGroupsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/ListSecurityGroupsCmd.java index f93e7b39586b..0de3bd5bdb69 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/ListSecurityGroupsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/ListSecurityGroupsCmd.java @@ -37,16 +37,16 @@ public class ListSecurityGroupsCmd extends BaseListTaggedResourcesCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.SECURITY_GROUP_NAME, type = CommandType.STRING, description = "lists security groups by name") + @Parameter(name = ApiConstants.SECURITY_GROUP_NAME, type = CommandType.STRING, description = "Lists security groups by name") private String securityGroupName; @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, - description = "lists security groups by virtual machine id", + description = "Lists security groups by Instance id", entityType = UserVmResponse.class) private Long virtualMachineId; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, description = "list the security group by the id provided", entityType = SecurityGroupResponse.class) + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, description = "List the security group by the id provided", entityType = SecurityGroupResponse.class) private Long id; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/RevokeSecurityGroupEgressCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/RevokeSecurityGroupEgressCmd.java index bf435406174c..91f2b7ad999a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/RevokeSecurityGroupEgressCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/RevokeSecurityGroupEgressCmd.java @@ -82,7 +82,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "revoking egress rule id: " + getId(); + return "Revoking egress rule with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/RevokeSecurityGroupIngressCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/RevokeSecurityGroupIngressCmd.java index c426647fe36c..2d7e591214df 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/RevokeSecurityGroupIngressCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/RevokeSecurityGroupIngressCmd.java @@ -83,7 +83,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "revoking ingress rule id: " + getId(); + return "Revoking ingress rule with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/UpdateSecurityGroupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/UpdateSecurityGroupCmd.java index 801fb6ac5e51..ea756927b039 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/UpdateSecurityGroupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/securitygroup/UpdateSecurityGroupCmd.java @@ -42,7 +42,7 @@ public class UpdateSecurityGroupCmd extends BaseCustomIdCmd { ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, required=true, description="The ID of the security group.", entityType=SecurityGroupResponse.class) + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, required=true, description = "The ID of the security group.", entityType=SecurityGroupResponse.class) private Long id; @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The new name of the security group.") diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/ArchiveSnapshotCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/ArchiveSnapshotCmd.java index f72de2278cc7..cae2a32a09fd 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/ArchiveSnapshotCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/ArchiveSnapshotCmd.java @@ -36,7 +36,7 @@ import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.context.CallContext; -@APICommand(name = "archiveSnapshot", description = "Archives (moves) a snapshot on primary storage to secondary storage", +@APICommand(name = "archiveSnapshot", description = "Archives (moves) a Snapshot on primary storage to secondary storage", responseObject = SnapshotResponse.class, entityType = {Snapshot.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ArchiveSnapshotCmd extends BaseAsyncCmd { @@ -44,7 +44,7 @@ public class ArchiveSnapshotCmd extends BaseAsyncCmd { @ACL(accessType = SecurityChecker.AccessType.OperateEntry) @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType = SnapshotResponse.class, - required=true, description="The ID of the snapshot") + required=true, description = "The ID of the Snapshot") private Long id; @Override @@ -54,18 +54,18 @@ public String getEventType() { @Override public String getEventDescription() { - return "Archiving snapshot " + id + " to secondary storage"; + return "Archiving Snapshot with ID: " + getResourceUuid(ApiConstants.ID) + " to secondary storage"; } @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { - CallContext.current().setEventDetails("Snapshot Id: " + this._uuidMgr.getUuid(Snapshot.class,getId())); + CallContext.current().setEventDetails("Snapshot ID: " + getResourceUuid(ApiConstants.ID)); Snapshot snapshot = _snapshotService.archiveSnapshot(getId()); if (snapshot != null) { SuccessResponse response = new SuccessResponse(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to archive snapshot"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to archive Snapshot"); } } 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 ac54ebbd8f8c..c67439a2ef7c 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 @@ -97,7 +97,11 @@ public class CopySnapshotCmd extends BaseAsyncCmd implements UserCmd { "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") + @Parameter (name = ApiConstants.USE_STORAGE_REPLICATION, + type=CommandType.BOOLEAN, + since = "4.21.0", + description = "Enables the snapshot to be copied to the supported primary storages when the config 'use.storage.replication' is set to true for the storage or globally. " + + "This is supported only for StorPool storage for now.") protected Boolean useStorageReplication; ///////////////////////////////////////////////////// @@ -140,18 +144,22 @@ public String getEventType() { @Override public String getEventDescription() { - StringBuilder descBuilder = new StringBuilder(); + StringBuilder descBuilder = new StringBuilder("Copying snapshot with ID: " + getResourceUuid(ApiConstants.ID)); + if (getDestinationZoneIds() != null) { + descBuilder.append(" to zones: ["); + for (Long destId : getDestinationZoneIds()) { - descBuilder.append(", "); descBuilder.append(_uuidMgr.getUuid(DataCenter.class, destId)); + descBuilder.append(", "); } - if (descBuilder.length() > 0) { - descBuilder.deleteCharAt(0); - } + + descBuilder.deleteCharAt(descBuilder.length() - 1); + + descBuilder.append("]"); } - return "copying snapshot: " + _uuidMgr.getUuid(Snapshot.class, getId()) + ((descBuilder.length() > 0) ? " to zones: " + descBuilder.toString() : ""); + return descBuilder.toString(); } @Override 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 60f3bbda8588..d03df501847a 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 @@ -49,7 +49,7 @@ import com.cloud.user.Account; import com.cloud.utils.exception.CloudRuntimeException; -@APICommand(name = "createSnapshot", description = "Creates an instant snapshot of a volume.", responseObject = SnapshotResponse.class, entityType = {Snapshot.class}, +@APICommand(name = "createSnapshot", description = "Creates an instant Snapshot of a volume.", responseObject = SnapshotResponse.class, entityType = {Snapshot.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateSnapshotCmd extends BaseAsyncCreateCmd { @@ -59,13 +59,13 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd { @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, - description = "The account of the snapshot. The account parameter must be used with the domainId parameter.") + description = "The Account of the Snapshot. The Account parameter must be used with the domainId parameter.") private String accountName; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "The domain ID of the snapshot. If used with the account parameter, specifies a domain for the account associated with the disk volume. If account is NOT provided then snapshot will be assigned to the caller account and domain.") + description = "The domain ID of the Snapshot. If used with the Account parameter, specifies a domain for the Account associated with the disk volume. If Account is NOT provided then snapshot will be assigned to the caller Account and domain.") private Long domainId; @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.UUID, entityType = VolumeResponse.class, required = true, description = "The ID of the disk volume") @@ -74,20 +74,20 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd { @Parameter(name = ApiConstants.POLICY_ID, type = CommandType.UUID, entityType = SnapshotPolicyResponse.class, - description = "policy id of the snapshot, if this is null, then use MANUAL_POLICY.") + description = "Policy ID of the Snapshot, if this is null, then use MANUAL_POLICY.") private Long policyId; - @Parameter(name = ApiConstants.SNAPSHOT_QUIESCEVM, type = CommandType.BOOLEAN, required = false, description = "quiesce vm if true") + @Parameter(name = ApiConstants.SNAPSHOT_QUIESCEVM, type = CommandType.BOOLEAN, required = false, description = "Quiesce Instance if true") private Boolean quiescevm; @Parameter(name = ApiConstants.LOCATION_TYPE, type = CommandType.STRING, required = false, description = "Currently applicable only for managed storage. " + "Valid location types: 'primary', 'secondary'. Default = 'primary'.") private String locationType; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the snapshot") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the Snapshot") private String snapshotName; - @Parameter(name = ApiConstants.ASYNC_BACKUP, type = CommandType.BOOLEAN, required = false, description = "asynchronous backup if true") + @Parameter(name = ApiConstants.ASYNC_BACKUP, type = CommandType.BOOLEAN, required = false, description = "Asynchronous backup if true") private Boolean asyncBackup; @Parameter(name = ApiConstants.TAGS, type = CommandType.MAP, description = "Map of tags (key/value pairs)") @@ -112,7 +112,10 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd { 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") + @Parameter (name = ApiConstants.USE_STORAGE_REPLICATION, + type=CommandType.BOOLEAN, + description = "Enables the snapshot to be copied to the supported primary storages when the config 'use.storage.replication' is set to true for the storage or globally. " + + "This is supported only for StorPool storage for now.") protected Boolean useStorageReplication; private String syncObjectType = BaseAsyncCmd.snapshotHostSyncObject; @@ -213,7 +216,7 @@ public long getEntityOwnerId() { " as it's no longer active"); } } else if (account.getState() == Account.State.DISABLED) { - throw new PermissionDeniedException("The owner of template is disabled: " + account); + throw new PermissionDeniedException("The owner of Template is disabled: " + account); } return volume.getAccountId(); @@ -226,7 +229,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "creating snapshot for volume: " + getVolumeUuid(); + return "Creating Snapshot for volume: " + getResourceUuid(ApiConstants.VOLUME_ID); } @Override @@ -241,7 +244,7 @@ public void create() throws ResourceAllocationException { setEntityId(snapshot.getId()); setEntityUuid(snapshot.getUuid()); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create snapshot for volume" + getVolumeUuid()); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Snapshot for volume" + getResourceUuid(ApiConstants.VOLUME_ID)); } } @@ -257,22 +260,21 @@ public void execute() { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Snapshot from volume [%s] was not found in database.", getVolumeUuid())); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Snapshot from volume [%s] was not found in database.", getResourceUuid(ApiConstants.VOLUME_ID))); } } catch (Exception e) { if (e.getCause() instanceof UnsupportedOperationException) { - throw new ServerApiException(ApiErrorCode.UNSUPPORTED_ACTION_ERROR, String.format("Failed to create snapshot due to unsupported operation: %s", e.getCause().getMessage())); + throw new ServerApiException(ApiErrorCode.UNSUPPORTED_ACTION_ERROR, String.format("Failed to create Snapshot due to unsupported operation: %s", e.getCause().getMessage())); } - String errorMessage = "Failed to create snapshot due to an internal error creating snapshot for volume " + getVolumeUuid(); + String errorMessage = "Failed to create Snapshot due to an internal error creating Snapshot for volume " + getResourceUuid(ApiConstants.VOLUME_ID); logger.error(errorMessage, e); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, errorMessage); } } public Snapshot.LocationType getLocationType() { - - if (Snapshot.LocationType.values() == null || Snapshot.LocationType.values().length == 0 || locationType == null) { + if (locationType == null) { return null; } @@ -310,8 +312,4 @@ public Boolean getAsyncBackup() { return asyncBackup; } } - - protected String getVolumeUuid() { - return _uuidMgr.getUuid(Volume.class, getVolumeId()); - } } 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 c99373688e5d..6fb5fb0463ab 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 @@ -39,7 +39,7 @@ import com.cloud.uservm.UserVm; import com.cloud.vm.snapshot.VMSnapshot; -@APICommand(name = "createSnapshotFromVMSnapshot", description = "Creates an instant snapshot of a volume from existing vm snapshot.", responseObject = SnapshotResponse.class, entityType = {Snapshot.class}, since = "4.10.0", +@APICommand(name = "createSnapshotFromVMSnapshot", description = "Creates an instant Snapshot of a volume from existing Instance Snapshot.", responseObject = SnapshotResponse.class, entityType = {Snapshot.class}, since = "4.10.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateSnapshotFromVMSnapshotCmd extends BaseAsyncCreateCmd { @@ -51,10 +51,10 @@ public class CreateSnapshotFromVMSnapshotCmd extends BaseAsyncCreateCmd { private Long volumeId; @Parameter(name=ApiConstants.VM_SNAPSHOT_ID, type=CommandType.UUID, entityType=VMSnapshotResponse.class, - required=true, description="The ID of the VM snapshot") + required=true, description = "The ID of the Instance Snapshot") private Long vmSnapshotId; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the snapshot") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the Snapshot") private String snapshotName; private String syncObjectType = BaseAsyncCmd.snapshotHostSyncObject; @@ -78,18 +78,18 @@ public String getSnapshotName() { private Long getVmId() { VMSnapshot vmsnapshot = _entityMgr.findById(VMSnapshot.class, getVMSnapshotId()); if (vmsnapshot == null) { - throw new InvalidParameterValueException("Unable to find vm snapshot by id=" + getVMSnapshotId()); + throw new InvalidParameterValueException("Unable to find Instance Snapshot by id=" + getVMSnapshotId()); } UserVm vm = _entityMgr.findById(UserVm.class, vmsnapshot.getVmId()); if (vm == null) { - throw new InvalidParameterValueException("Unable to find vm by vm snapshot id=" + getVMSnapshotId()); + throw new InvalidParameterValueException("Unable to find Instance by Instance Snapshot id=" + getVMSnapshotId()); } return vm.getId(); } private Long getHostId() { VMSnapshot vmsnapshot = _entityMgr.findById(VMSnapshot.class, getVMSnapshotId()); if (vmsnapshot == null) { - throw new InvalidParameterValueException("Unable to find vm snapshot by id=" + getVMSnapshotId()); + throw new InvalidParameterValueException("Unable to find Instance Snapshot by id=" + getVMSnapshotId()); } UserVm vm = _entityMgr.findById(UserVm.class, vmsnapshot.getVmId()); if (vm != null) { @@ -124,13 +124,13 @@ public long getEntityOwnerId() { if (account.getType() == Account.Type.PROJECT) { Project project = _projectService.findByProjectAccountId(vmsnapshot.getAccountId()); if (project == null) { - throw new InvalidParameterValueException(String.format("Unable to find project by account %s", account)); + throw new InvalidParameterValueException(String.format("Unable to find project by Account %s", account)); } if (project.getState() != Project.State.Active) { throw new PermissionDeniedException(String.format("Can't add resources to the project %s in state=%s as it's no longer active", project, project.getState())); } } else if (account.getState() == Account.State.DISABLED) { - throw new PermissionDeniedException("The owner of template is disabled: " + account); + throw new PermissionDeniedException("The owner of Template is disabled: " + account); } return vmsnapshot.getAccountId(); @@ -143,7 +143,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "creating snapshot from vm snapshot : " + this._uuidMgr.getUuid(VMSnapshot.class, getVMSnapshotId()); + return "Creating Snapshot from Instance Snapshot : " + getResourceUuid(ApiConstants.VOLUME_ID); } @Override @@ -158,15 +158,15 @@ public void create() throws ResourceAllocationException { this.setEntityId(snapshot.getId()); this.setEntityUuid(snapshot.getUuid()); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create snapshot from vm snapshot"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Snapshot from Instance Snapshot"); } } @Override public void execute() { VMSnapshot vmSnapshot = _vmSnapshotService.getVMSnapshotById(getVMSnapshotId()); - logger.info("CreateSnapshotFromVMSnapshotCmd with vm snapshot {} with id {} and snapshot [id: {}, uuid: {}]", vmSnapshot, getVMSnapshotId(), getEntityId(), getEntityUuid()); - CallContext.current().setEventDetails("Vm Snapshot Id: " + vmSnapshot.getUuid()); + logger.info("CreateSnapshotFromVMSnapshotCmd with {} and Snapshot [ID: {}, UUID: {}]", vmSnapshot, getEntityId(), getEntityUuid()); + CallContext.current().setEventDetails("Instance Snapshot ID: " + vmSnapshot.getUuid()); Snapshot snapshot = null; try { snapshot = _snapshotService.backupSnapshotFromVmSnapshot(getEntityId(), getVmId(), getVolumeId(), getVMSnapshotId()); @@ -175,19 +175,19 @@ public void execute() { response.setResponseName(getCommandName()); this.setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to create snapshot due to an internal error creating snapshot from vm snapshot %s", vmSnapshot)); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to create Snapshot due to an internal error creating Snapshot from Instance Snapshot %s", vmSnapshot)); } } catch (InvalidParameterValueException ex) { throw ex; } catch (Exception e) { logger.debug("Failed to create snapshot", e); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to create snapshot due to an internal error creating snapshot from vm snapshot %s", vmSnapshot)); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to create Snapshot due to an internal error creating Snapshot from Instance Snapshot %s", vmSnapshot)); } finally { if (snapshot == null) { try { _snapshotService.deleteSnapshot(getEntityId(), null); } catch (Exception e) { - logger.debug("Failed to clean failed snapshot {} with id {}", () -> _entityMgr.findById(Snapshot.class, getEntityId()), this::getEntityId); + logger.debug("Failed to clean failed Snapshot {} with ID {}", () -> _entityMgr.findById(Snapshot.class, getEntityId()), this::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 66089894737d..b1e7b2a00040 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 @@ -43,7 +43,7 @@ import java.util.Map; import org.apache.commons.lang3.BooleanUtils; -@APICommand(name = "createSnapshotPolicy", description = "Creates a snapshot policy for the account.", responseObject = SnapshotPolicyResponse.class, +@APICommand(name = "createSnapshotPolicy", description = "Creates a Snapshot policy for the account.", responseObject = SnapshotPolicyResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateSnapshotPolicyCmd extends BaseCmd { @@ -52,13 +52,13 @@ public class CreateSnapshotPolicyCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.INTERVAL_TYPE, type = CommandType.STRING, required = true, description = "valid values are HOURLY, DAILY, WEEKLY, and MONTHLY") + @Parameter(name = ApiConstants.INTERVAL_TYPE, type = CommandType.STRING, required = true, description = "Valid values are HOURLY, DAILY, WEEKLY, and MONTHLY") private String intervalType; - @Parameter(name = ApiConstants.MAX_SNAPS, type = CommandType.INTEGER, required = true, description = "maximum number of snapshots to retain") + @Parameter(name = ApiConstants.MAX_SNAPS, type = CommandType.INTEGER, required = true, description = "Maximum number of Snapshots to retain") private Integer maxSnaps; - @Parameter(name = ApiConstants.SCHEDULE, type = CommandType.STRING, required = true, description = "time the snapshot is scheduled to be taken. " + "Format is:" + @Parameter(name = ApiConstants.SCHEDULE, type = CommandType.STRING, required = true, description = "Time the Snapshot is scheduled to be taken. " + "Format is:" + "* if HOURLY, MM" + "* if DAILY, MM:HH" + "* if WEEKLY, MM:HH:DD (1-7)" + "* if MONTHLY, MM:HH:DD (1-28)") private String schedule; @@ -68,10 +68,10 @@ public class CreateSnapshotPolicyCmd extends BaseCmd { description = "Specifies a timezone for this command. For more information on the timezone parameter, see Time Zone Format.") private String timezone; - @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.UUID, entityType = VolumeResponse.class, required = true, description = "the ID of the disk volume") + @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.UUID, entityType = VolumeResponse.class, required = true, description = "The ID of the disk volume") private Long volumeId; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the policy to the end user or not", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "An optional field, whether to the display the policy to the end user or not", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; @Parameter(name = ApiConstants.TAGS, type = CommandType.MAP, description = "Map of tags (key/value pairs)") @@ -94,7 +94,11 @@ public class CreateSnapshotPolicyCmd extends BaseCmd { 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") + @Parameter (name = ApiConstants.USE_STORAGE_REPLICATION, + type=CommandType.BOOLEAN, + since = "4.21.0", + description = "Enables the snapshot to be copied to the supported primary storages when the config 'use.storage.replication' is set to true for the storage or globally. " + + "This is supported only for StorPool storage for now.") protected Boolean useStorageReplication; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -162,7 +166,7 @@ public long getEntityOwnerId() { throw ex; } } else if (account.getState() == Account.State.DISABLED) { - throw new PermissionDeniedException("The owner of template is disabled: " + account); + throw new PermissionDeniedException("The owner of Template is disabled: " + account); } return volume.getAccountId(); @@ -188,7 +192,7 @@ public void execute() { response.setResponseName(getCommandName()); this.setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create snapshot policy"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Snapshot policy"); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/DeleteSnapshotCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/DeleteSnapshotCmd.java index a0a8cfac9bd2..b4eaceb61ba6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/DeleteSnapshotCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/DeleteSnapshotCmd.java @@ -35,7 +35,7 @@ import com.cloud.storage.Snapshot; import com.cloud.user.Account; -@APICommand(name = "deleteSnapshot", description = "Deletes a snapshot of a disk volume.", responseObject = SuccessResponse.class, entityType = {Snapshot.class}, +@APICommand(name = "deleteSnapshot", description = "Deletes a Snapshot of a disk volume.", responseObject = SuccessResponse.class, entityType = {Snapshot.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DeleteSnapshotCmd extends BaseAsyncCmd { @@ -45,7 +45,7 @@ public class DeleteSnapshotCmd extends BaseAsyncCmd { @ACL(accessType = AccessType.OperateEntry) @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType = SnapshotResponse.class, - required=true, description="The ID of the snapshot") + required=true, description = "The ID of the Snapshot") private Long id; @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.UUID, entityType = ZoneResponse.class, description="The ID of the zone for the snapshot", since = "4.19.0") @@ -85,7 +85,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "deleting snapshot: " + this._uuidMgr.getUuid(Snapshot.class, getId()); + return "Deleting Snapshot with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -100,13 +100,13 @@ public Long getApiResourceId() { @Override public void execute() { - CallContext.current().setEventDetails("Snapshot Id: " + this._uuidMgr.getUuid(Snapshot.class, getId())); + CallContext.current().setEventDetails("Snapshot ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _snapshotService.deleteSnapshot(getId(), getZoneId()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete snapshot"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete Snapshot"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/DeleteSnapshotPoliciesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/DeleteSnapshotPoliciesCmd.java index 6f4b60dc8b21..79ac80f78e86 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/DeleteSnapshotPoliciesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/DeleteSnapshotPoliciesCmd.java @@ -30,7 +30,7 @@ import com.cloud.user.Account; -@APICommand(name = "deleteSnapshotPolicies", description = "Deletes snapshot policies for the account.", responseObject = SuccessResponse.class, +@APICommand(name = "deleteSnapshotPolicies", description = "Deletes Snapshot policies for the account.", responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DeleteSnapshotPoliciesCmd extends BaseCmd { @@ -39,14 +39,14 @@ public class DeleteSnapshotPoliciesCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SnapshotPolicyResponse.class, description = "the Id of the snapshot policy") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SnapshotPolicyResponse.class, description = "The ID of the Snapshot policy") private Long id; @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = SnapshotPolicyResponse.class, - description = "list of snapshots policy IDs separated by comma") + description = "List of Snapshots policy IDs separated by comma") private List ids; ///////////////////////////////////////////////////// @@ -77,7 +77,7 @@ public void execute() { SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete snapshot policy"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete Snapshot policy"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/ExtractSnapshotCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/ExtractSnapshotCmd.java index 3f0f82ea4e3b..dacdd20b3969 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/ExtractSnapshotCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/ExtractSnapshotCmd.java @@ -96,12 +96,12 @@ public String getEventType() { @Override public String getEventDescription() { - return "Snapshot extraction job"; + return "Starting Snapshot extraction for Snapshot with ID: " + getResourceUuid(ApiConstants.ID); } @Override public void execute() { - CallContext.current().setEventDetails("Snapshot ID: " + this._uuidMgr.getUuid(Snapshot.class, getId())); + CallContext.current().setEventDetails("Snapshot ID: " + getResourceUuid(ApiConstants.ID)); String uploadUrl = _snapshotService.extractSnapshot(this); logger.info("Extract URL [{}] of snapshot [{}].", uploadUrl, id); if (uploadUrl != null) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/ListSnapshotPoliciesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/ListSnapshotPoliciesCmd.java index 126a4080e6d2..df73a009a035 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/ListSnapshotPoliciesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/ListSnapshotPoliciesCmd.java @@ -23,7 +23,7 @@ import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.SnapshotPolicyResponse; @@ -32,22 +32,22 @@ import com.cloud.storage.snapshot.SnapshotPolicy; import com.cloud.utils.Pair; -@APICommand(name = "listSnapshotPolicies", description = "Lists snapshot policies.", responseObject = SnapshotPolicyResponse.class, +@APICommand(name = "listSnapshotPolicies", description = "Lists Snapshot policies.", responseObject = SnapshotPolicyResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class ListSnapshotPoliciesCmd extends BaseListCmd { +public class ListSnapshotPoliciesCmd extends BaseListProjectAndAccountResourcesCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.UUID, entityType = VolumeResponse.class, description = "the ID of the disk volume") + @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.UUID, entityType = VolumeResponse.class, description = "The ID of the disk volume") private Long volumeId; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SnapshotPolicyResponse.class, description = "the ID of the snapshot policy") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SnapshotPolicyResponse.class, description = "The ID of the Snapshot policy") private Long id; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; ///////////////////////////////////////////////////// @@ -69,13 +69,14 @@ public boolean isDisplay() { public Long getId() { return id; } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @Override public void execute() { - Pair, Integer> result = _snapshotService.listPoliciesforVolume(this); + Pair, Integer> result = _snapshotService.listSnapshotPolicies(this); ListResponse response = new ListResponse(); List policyResponses = new ArrayList(); for (SnapshotPolicy policy : result.first()) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/ListSnapshotsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/ListSnapshotsCmd.java index 826c54c2e052..316b9c8d0c9d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/ListSnapshotsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/ListSnapshotsCmd.java @@ -31,7 +31,7 @@ import com.cloud.storage.Snapshot; -@APICommand(name = "listSnapshots", description = "Lists all available snapshots for the account.", responseObject = SnapshotResponse.class, entityType = { +@APICommand(name = "listSnapshots", description = "Lists all available Snapshots for the Account.", responseObject = SnapshotResponse.class, entityType = { Snapshot.class }, responseView = ResponseObject.ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListSnapshotsCmd extends BaseListTaggedResourcesCmd { @@ -40,25 +40,25 @@ public class ListSnapshotsCmd extends BaseListTaggedResourcesCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SnapshotResponse.class, description = "lists snapshot by snapshot ID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SnapshotResponse.class, description = "Lists Snapshot by Snapshot ID") private Long id; - @Parameter(name=ApiConstants.IDS, type=CommandType.LIST, collectionType=CommandType.UUID, entityType=SnapshotResponse.class, description="the IDs of the snapshots, mutually exclusive with id", since = "4.9") + @Parameter(name=ApiConstants.IDS, type=CommandType.LIST, collectionType=CommandType.UUID, entityType=SnapshotResponse.class, description = "The IDs of the Snapshots, mutually exclusive with id", since = "4.9") private List ids; - @Parameter(name = ApiConstants.INTERVAL_TYPE, type = CommandType.STRING, description = "valid values are HOURLY, DAILY, WEEKLY, and MONTHLY.") + @Parameter(name = ApiConstants.INTERVAL_TYPE, type = CommandType.STRING, description = "Valid values are HOURLY, DAILY, WEEKLY, and MONTHLY.") private String intervalType; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "lists snapshot by snapshot name") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Lists Snapshot by Snapshot name") private String snapshotName; - @Parameter(name = ApiConstants.SNAPSHOT_TYPE, type = CommandType.STRING, description = "valid values are MANUAL or RECURRING.") + @Parameter(name = ApiConstants.SNAPSHOT_TYPE, type = CommandType.STRING, description = "Valid values are MANUAL or RECURRING.") private String snapshotType; - @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.UUID, entityType = VolumeResponse.class, description = "the ID of the disk volume") + @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.UUID, entityType = VolumeResponse.class, description = "The ID of the disk volume") private Long volumeId; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "list snapshots by zone id") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "List Snapshots by zone id") private Long zoneId; @Parameter(name = ApiConstants.SHOW_UNIQUE, type = CommandType.BOOLEAN, description = "If set to false, list templates across zones and their storages", since = "4.19.0") diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/RevertSnapshotCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/RevertSnapshotCmd.java index fe3b4da0160e..59881dfdabe2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/RevertSnapshotCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/RevertSnapshotCmd.java @@ -35,7 +35,7 @@ import com.cloud.storage.Snapshot; import com.cloud.user.Account; -@APICommand(name = "revertSnapshot", description = "This is supposed to revert a volume snapshot. This command is only supported with KVM so far", responseObject = SnapshotResponse.class, entityType = {Snapshot.class}, +@APICommand(name = "revertSnapshot", description = "This is supposed to revert a volume Snapshot. This command is only supported with KVM so far", responseObject = SnapshotResponse.class, entityType = {Snapshot.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class RevertSnapshotCmd extends BaseAsyncCmd { @@ -44,7 +44,7 @@ public class RevertSnapshotCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) @Parameter(name= ApiConstants.ID, type= BaseCmd.CommandType.UUID, entityType = SnapshotResponse.class, - required=true, description="The ID of the snapshot") + required=true, description = "The ID of the Snapshot") private Long id; ///////////////////////////////////////////////////// @@ -74,7 +74,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "revert snapshot: " + this._uuidMgr.getUuid(Snapshot.class, getId()); + return "Reverting Snapshot with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -89,14 +89,14 @@ public Long getApiResourceId() { @Override public void execute() { - CallContext.current().setEventDetails("Snapshot Id: " + this._uuidMgr.getUuid(Snapshot.class, getId())); + CallContext.current().setEventDetails("Snapshot ID: " + getResourceUuid(ApiConstants.ID)); Snapshot snapshot = _snapshotService.revertSnapshot(getId()); if (snapshot != null) { SnapshotResponse response = _responseGenerator.createSnapshotResponse(snapshot); response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to revert snapshot"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to revert Snapshot"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/UpdateSnapshotPolicyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/UpdateSnapshotPolicyCmd.java index e7feb11f4afd..84f8d0b3c390 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/UpdateSnapshotPolicyCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/UpdateSnapshotPolicyCmd.java @@ -35,7 +35,7 @@ import org.apache.cloudstack.context.CallContext; -@APICommand(name = "updateSnapshotPolicy", description = "Updates the snapshot policy.", responseObject = SnapshotPolicyResponse.class, responseView = ResponseObject.ResponseView.Restricted, entityType = {Volume.class}, +@APICommand(name = "updateSnapshotPolicy", description = "Updates the Snapshot policy.", responseObject = SnapshotPolicyResponse.class, responseView = ResponseObject.ResponseView.Restricted, entityType = {Volume.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class UpdateSnapshotPolicyCmd extends BaseAsyncCustomIdCmd { @@ -44,12 +44,12 @@ public class UpdateSnapshotPolicyCmd extends BaseAsyncCustomIdCmd { ///////////////////////////////////////////////////// @ACL(accessType = SecurityChecker.AccessType.OperateEntry) - @Parameter(name= ApiConstants.ID, type=CommandType.UUID, entityType=SnapshotPolicyResponse.class, description="the ID of the snapshot policy") + @Parameter(name= ApiConstants.ID, type=CommandType.UUID, entityType=SnapshotPolicyResponse.class, description = "The ID of the Snapshot policy") private Long id; @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, - description = "an optional field, whether to the display the snapshot policy to the end user or not.", + description = "An optional field, whether to the display the Snapshot policy to the end user or not.", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; @@ -80,11 +80,11 @@ public long getEntityOwnerId() { SnapshotPolicy policy = _entityMgr.findById(SnapshotPolicy.class, getId()); if (policy == null) { - throw new InvalidParameterValueException("Invalid snapshot policy id was provided"); + throw new InvalidParameterValueException("Invalid Snapshot policy ID was provided"); } Volume volume = _responseGenerator.findVolumeById(policy.getVolumeId()); if (volume == null) { - throw new InvalidParameterValueException("Snapshot policy's volume id doesn't exist"); + throw new InvalidParameterValueException("Snapshot policy's volume ID doesn't exist"); }else{ return volume.getAccountId(); } @@ -97,21 +97,21 @@ public String getEventType() { @Override public String getEventDescription() { - StringBuilder desc = new StringBuilder("Updating snapshot policy: "); + StringBuilder desc = new StringBuilder("Updating Snapshot policy: "); desc.append(getId()); return desc.toString(); } @Override public void execute() { - CallContext.current().setEventDetails("SnapshotPolicy Id: " + getId()); + CallContext.current().setEventDetails("Snapshot policy ID: " + getResourceUuid(ApiConstants.ID)); SnapshotPolicy result = _snapshotService.updateSnapshotPolicy(this); if (result != null) { SnapshotPolicyResponse response = _responseGenerator.createSnapshotPolicyResponse(result); response.setResponseName(getCommandName()); this.setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update snapshot policy"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update Snapshot policy"); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/ssh/CreateSSHKeyPairCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/ssh/CreateSSHKeyPairCmd.java index 5212779e9654..1b79c11644fb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/ssh/CreateSSHKeyPairCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/ssh/CreateSSHKeyPairCmd.java @@ -40,16 +40,16 @@ public class CreateSSHKeyPairCmd extends BaseCmd { private String name; //Owner information - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the ssh key. Must be used with domainId.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "An optional Account for the SSH key. 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 ssh key. If the account parameter is used, domainId must also be used.") + description = "An optional domainId for the SSH key. 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 ssh key") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "An optional project for the SSH key") private Long projectId; ///////////////////////////////////////////////////// @@ -77,7 +77,7 @@ public Long getProjectId() { ///////////////////////////////////////////////////// @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/ssh/DeleteSSHKeyPairCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/ssh/DeleteSSHKeyPairCmd.java index 364ca77ae1fc..4ed8664a79d7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/ssh/DeleteSSHKeyPairCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/ssh/DeleteSSHKeyPairCmd.java @@ -41,13 +41,13 @@ public class DeleteSSHKeyPairCmd extends BaseCmd { @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the keypair") private String name; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "the account associated with the keypair. Must be used with the domainId parameter.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "The Account associated with the keypair. Must be used with the domainId parameter.") private String accountName; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "the domain ID associated with the keypair") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "The domain ID associated with the keypair") private Long domainId; - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "the project associated with keypair") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "The project associated with keypair") private Long projectId; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/ssh/ListSSHKeyPairsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/ssh/ListSSHKeyPairsCmd.java index 6bf8dca864b0..343db1bc9565 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/ssh/ListSSHKeyPairsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/ssh/ListSSHKeyPairsCmd.java @@ -37,7 +37,7 @@ public class ListSSHKeyPairsCmd extends BaseListProjectAndAccountResourcesCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SSHKeyPairResponse.class, description = "the ID of the ssh keypair") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SSHKeyPairResponse.class, description = "The ID of the SSH keypair") private Long id; @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "A key pair name to look for") diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/ssh/RegisterSSHKeyPairCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/ssh/RegisterSSHKeyPairCmd.java index 6a0c0541bb4f..f7af86d08357 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/ssh/RegisterSSHKeyPairCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/ssh/RegisterSSHKeyPairCmd.java @@ -43,16 +43,16 @@ public class RegisterSSHKeyPairCmd extends BaseCmd { private String publicKey; //Owner information - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the ssh key. Must be used with domainId.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "An optional Account for the SSH key. 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 ssh key. If the account parameter is used, domainId must also be used.") + description = "An optional domainId for the SSH key. 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 ssh key") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "An optional project for the SSH key") private Long projectId; ///////////////////////////////////////////////////// @@ -85,7 +85,7 @@ public Long getProjectId() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ChangeSharedFSDiskOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ChangeSharedFSDiskOfferingCmd.java index b078ce4aae95..24290bc345e1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ChangeSharedFSDiskOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ChangeSharedFSDiskOfferingCmd.java @@ -117,7 +117,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Changing disk offering for the Shared FileSystem " + id; + return "Changing disk offering for the Shared FileSystem with ID:" + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ChangeSharedFSServiceOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ChangeSharedFSServiceOfferingCmd.java index 70fb543d64c3..1ac0f27067b4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ChangeSharedFSServiceOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ChangeSharedFSServiceOfferingCmd.java @@ -96,7 +96,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Changing service offering for the Shared FileSystem " + id; + return "Changing service offering for the Shared FileSystem with ID:" + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/CreateSharedFSCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/CreateSharedFSCmd.java index ddaa31612a89..595b611b5c0d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/CreateSharedFSCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/CreateSharedFSCmd.java @@ -230,7 +230,7 @@ public Long getApiResourceId() { } @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/DestroySharedFSCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/DestroySharedFSCmd.java index 09fae53f1284..35f16a4dc2a0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/DestroySharedFSCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/DestroySharedFSCmd.java @@ -95,7 +95,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Destroying Shared FileSystem " + id; + return "Destroying Shared FileSystem with ID:" + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ExpungeSharedFSCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ExpungeSharedFSCmd.java index 39b99218b667..8960aa3e4d40 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ExpungeSharedFSCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ExpungeSharedFSCmd.java @@ -74,7 +74,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Expunging Shared FileSystem " + id; + return "Expunging Shared FileSystem with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/RestartSharedFSCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/RestartSharedFSCmd.java index 576c472b6eb2..75565796caa4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/RestartSharedFSCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/RestartSharedFSCmd.java @@ -94,7 +94,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Restarting Shared FileSystem " + id; + return "Restarting Shared FileSystem with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/StartSharedFSCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/StartSharedFSCmd.java index bd384aceef73..d7440b532b31 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/StartSharedFSCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/StartSharedFSCmd.java @@ -84,7 +84,7 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - return "Starting Shared FileSystem " + id; + return "Starting Shared FileSystem with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/StopSharedFSCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/StopSharedFSCmd.java index d6e0737144a5..3800b16289e7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/StopSharedFSCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/StopSharedFSCmd.java @@ -92,7 +92,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Stopping Shared FileSystem " + id; + return "Stopping Shared FileSystem with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/tag/CreateTagsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/tag/CreateTagsCmd.java index 30904db46c44..6350baefe9a5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/tag/CreateTagsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/tag/CreateTagsCmd.java @@ -46,17 +46,17 @@ public class CreateTagsCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.TAGS, type = CommandType.MAP, required = true, description = "Map of tags (key/value pairs)") private Map tag; - @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, required = true, description = "type of the resource") + @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, required = true, description = "Type of the resource") private String resourceType; @Parameter(name = ApiConstants.RESOURCE_IDS, type = CommandType.LIST, required = true, collectionType = CommandType.STRING, - description = "list of resources to create the tags for") + description = "List of resources to create the tags for") private List resourceIds; - @Parameter(name = ApiConstants.CUSTOMER, type = CommandType.STRING, description = "identifies client specific tag. " + @Parameter(name = ApiConstants.CUSTOMER, type = CommandType.STRING, description = "Identifies client specific tag. " + "When the value is not null, the tag can't be used by cloudStack code internally") private String customer; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/tag/ListTagsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/tag/ListTagsCmd.java index 8b3c83aa20a6..2590f7d67b7b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/tag/ListTagsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/tag/ListTagsCmd.java @@ -30,19 +30,19 @@ requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListTagsCmd extends BaseListProjectAndAccountResourcesCmd { - @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, description = "list by resource type") + @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, description = "List by resource type") private String resourceType; - @Parameter(name = ApiConstants.RESOURCE_ID, type = CommandType.STRING, description = "list by resource id") + @Parameter(name = ApiConstants.RESOURCE_ID, type = CommandType.STRING, description = "List by resource ID") private String resourceId; - @Parameter(name = ApiConstants.KEY, type = CommandType.STRING, description = "list by key") + @Parameter(name = ApiConstants.KEY, type = CommandType.STRING, description = "List by key") private String key; - @Parameter(name = ApiConstants.VALUE, type = CommandType.STRING, description = "list by value") + @Parameter(name = ApiConstants.VALUE, type = CommandType.STRING, description = "List by value") private String value; - @Parameter(name = ApiConstants.CUSTOMER, type = CommandType.STRING, description = "list by customer name") + @Parameter(name = ApiConstants.CUSTOMER, type = CommandType.STRING, description = "List by customer name") private String customer; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/CopyTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/CopyTemplateCmd.java index f094bc435070..02601b2257f1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/CopyTemplateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/CopyTemplateCmd.java @@ -39,7 +39,7 @@ import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.Account; -@APICommand(name = "copyTemplate", description = "Copies a template from one zone to another.", responseObject = TemplateResponse.class, responseView = ResponseView.Restricted, +@APICommand(name = "copyTemplate", description = "Copies a Template from one zone to another.", responseObject = TemplateResponse.class, responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CopyTemplateCmd extends BaseAsyncCmd implements UserCmd { private static final String s_name = "copytemplateresponse"; @@ -52,7 +52,7 @@ public class CopyTemplateCmd extends BaseAsyncCmd implements UserCmd { type = CommandType.UUID, entityType = ZoneResponse.class, required = false, - description = "ID of the zone the template is being copied to.") + description = "ID of the zone the Template is being copied to.") protected Long destZoneId; @Parameter(name = ApiConstants.ID, type = CommandType.UUID, @@ -62,9 +62,9 @@ public class CopyTemplateCmd extends BaseAsyncCmd implements UserCmd { @Parameter(name = ApiConstants.SOURCE_ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, - description = "ID of the zone the template is currently hosted on. " + - "If not specified and template is cross-zone, " + - "then we will sync this template to region wide image store.") + description = "ID of the zone the Template is currently hosted on. " + + "If not specified and Template is cross-zone, " + + "then we will sync this Template to region wide image store.") private Long sourceZoneId; @Parameter(name = ApiConstants.DESTINATION_ZONE_ID_LIST, @@ -72,8 +72,8 @@ public class CopyTemplateCmd extends BaseAsyncCmd implements UserCmd { collectionType = CommandType.UUID, entityType = ZoneResponse.class, required = false, - description = "A list of IDs of the zones that the template needs to be copied to." + - "Specify this list if the template needs to copied to multiple zones in one go. " + + description = "A list of IDs of the zones that the Template needs to be copied to." + + "Specify this list if the Template needs to copied to multiple zones in one go. " + "Do not specify destzoneid and destzoneids together, however one of them is required.") protected List destZoneIds; @@ -136,19 +136,21 @@ public String getEventType() { @Override public String getEventDescription() { - StringBuilder descBuilder = new StringBuilder(); - if (getDestinationZoneIds() != null) { + String description = "Copying Template: " + getResourceUuid(ApiConstants.ID); + + if (getSourceZoneId() != null) { + description += " from zone: " + getResourceUuid(ApiConstants.SOURCE_ZONE_ID); + } + if (getDestinationZoneIds() != null) { + description += " to zones: "; for (Long destId : getDestinationZoneIds()) { - descBuilder.append(", "); - descBuilder.append(this._uuidMgr.getUuid(DataCenter.class, destId)); - } - if (descBuilder.length() > 0) { - descBuilder.deleteCharAt(0); + description += this._uuidMgr.getUuid(DataCenter.class, destId); + description += ", "; } } - return "copying template: " + this._uuidMgr.getUuid(VirtualMachineTemplate.class, getId()) +((getSourceZoneId() != null) ? " from zone: " + this._uuidMgr.getUuid(DataCenter.class, getSourceZoneId()) : "") + ((descBuilder.length() > 0) ? " to zones: " + descBuilder.toString() : ""); + return description; } @Override @@ -186,7 +188,7 @@ public void execute() throws ResourceAllocationException { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to copy template"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to copy Template"); } } catch (StorageUnavailableException ex) { logger.warn("Exception: ", ex); 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 5f09ac6698d6..b5e41ff449ca 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 @@ -51,8 +51,8 @@ import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.Account; -@APICommand(name = "createTemplate", responseObject = TemplateResponse.class, description = "Creates a template of a virtual machine. " + "The virtual machine must be in a STOPPED state. " - + "A template created from this command is automatically designated as a private template visible to the account that created it.", responseView = ResponseView.Restricted, +@APICommand(name = "createTemplate", responseObject = TemplateResponse.class, description = "Creates a Template of an Instance. " + "The Instance must be in a STOPPED state. " + + "A Template created from this command is automatically designated as a private Template visible to the account that created it.", responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateTemplateCmd extends BaseAsyncCreateCmd implements UserCmd { private static final String s_name = "createtemplateresponse"; @@ -66,60 +66,60 @@ public class CreateTemplateCmd extends BaseAsyncCreateCmd implements UserCmd { @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, - description = "The display text of the template, defaults to the 'name'.", + description = "The display text of the Template, defaults to the 'name'.", length = 4096) private String displayText; - @Parameter(name = ApiConstants.IS_FEATURED, type = CommandType.BOOLEAN, description = "true if this template is a featured template, false otherwise") + @Parameter(name = ApiConstants.IS_FEATURED, type = CommandType.BOOLEAN, description = "True if this Template is a featured Template, false otherwise") private Boolean featured; - @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "true if this template is a public template, false otherwise") + @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "True if this Template is a public Template, false otherwise") private Boolean publicTemplate; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the template") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "The name of the Template") private String templateName; @Parameter(name = ApiConstants.OS_TYPE_ID, type = CommandType.UUID, entityType = GuestOSResponse.class, required = true, - description = "the ID of the OS Type that best represents the OS of this template.") + description = "The ID of the OS Type that best represents the OS of this Template.") private Long osTypeId; @Parameter(name = ApiConstants.PASSWORD_ENABLED, type = CommandType.BOOLEAN, - description = "true if the template supports the password reset feature; default is false") + description = "True if the Template supports the password reset feature; default is false") private Boolean passwordEnabled; - @Parameter(name = ApiConstants.SSHKEY_ENABLED, type = CommandType.BOOLEAN, description = "true if the template supports the sshkey upload feature; default is false") + @Parameter(name = ApiConstants.SSHKEY_ENABLED, type = CommandType.BOOLEAN, description = "True if the Template supports the SSHkey upload feature; default is false") private Boolean sshKeyEnabled; - @Parameter(name = ApiConstants.REQUIRES_HVM, type = CommandType.BOOLEAN, description = "true if the template requires HVM, false otherwise") + @Parameter(name = ApiConstants.REQUIRES_HVM, type = CommandType.BOOLEAN, description = "True if the Template requires HVM, false otherwise") private Boolean requiresHvm; @Parameter(name = ApiConstants.SNAPSHOT_ID, type = CommandType.UUID, entityType = SnapshotResponse.class, - description = "the ID of the snapshot the template is being created from. Either this parameter, or volumeId has to be passed in") + description = "The ID of the Snapshot the Template is being created from. Either this parameter, or volumeId has to be passed in") protected Long snapshotId; @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.UUID, entityType = VolumeResponse.class, - description = "the ID of the disk volume the template is being created from. Either this parameter, or snapshotId has to be passed in") + description = "The ID of the disk volume the Template is being created from. Either this parameter, or snapshotId has to be passed in") protected Long volumeId; @Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.UUID, entityType = UserVmResponse.class, - description="Optional, VM ID. If this presents, it is going to create a baremetal template for VM this ID refers to. This is only for VM whose hypervisor type is BareMetal") + description = "Optional, Instance ID. If this presents, it is going to create a baremetal Template for Instance this ID refers to. This is only for Instance whose hypervisor type is BareMetal") protected Long vmId; @Parameter(name = ApiConstants.URL, type = CommandType.STRING, length = 2048, - description = "Optional, only for baremetal hypervisor. The directory name where template stored on CIFS server") + description = "Optional, only for baremetal hypervisor. The directory name where Template stored on CIFS server") private String url; - @Parameter(name = ApiConstants.TEMPLATE_TAG, type = CommandType.STRING, description = "the tag for this template.") + @Parameter(name = ApiConstants.TEMPLATE_TAG, type = CommandType.STRING, description = "The tag for this Template.") private String templateTag; @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "Template details in key/value pairs using format details[i].keyname=keyvalue. Example: details[0].hypervisortoolsversion=xenserver61") @@ -127,10 +127,10 @@ public class CreateTemplateCmd extends BaseAsyncCreateCmd implements UserCmd { @Parameter(name = ApiConstants.IS_DYNAMICALLY_SCALABLE, type = CommandType.BOOLEAN, - description = "true if template contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory") + description = "True if Template contains XS/VMWare tools in order to support dynamic scaling of Instance CPU/memory") protected Boolean isDynamicallyScalable; - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "create template for the project") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Create Template for the project") private Long projectId; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the zone for the template. Can be specified with snapshot only", since = "4.19.0") @@ -150,7 +150,7 @@ public class CreateTemplateCmd extends BaseAsyncCreateCmd implements UserCmd { 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", + description = "the CPU arch of the template. Valid options are: x86_64, aarch64, s390x. Defaults to x86_64", since = "4.20.2") private String arch; @@ -211,7 +211,7 @@ public String getUrl() { } public String getTemplateTag() { - return templateTag; + return StringUtils.isBlank(templateTag) ? null : templateTag; } public Map getDetails() { @@ -272,7 +272,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "creating template: " + getTemplateName(); + return "Creating Template: " + getTemplateName(); } @Override @@ -293,7 +293,7 @@ public void create() throws ResourceAllocationException { setEntityId(template.getId()); setEntityUuid(template.getUuid()); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create a template"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create a Template"); } } @@ -301,7 +301,7 @@ public void create() throws ResourceAllocationException { @Override public void execute() { CallContext.current().setEventDetails( - "Template Id: " + getEntityUuid() + ((getSnapshotId() == null) ? " from volume Id: " + this._uuidMgr.getUuid(Volume.class, getVolumeId()) : " from snapshot Id: " + this._uuidMgr.getUuid(Snapshot.class, getSnapshotId()))); + "Template ID: " + getEntityUuid() + ((getSnapshotId() == null) ? " from volume with ID: " + getResourceUuid(ApiConstants.VOLUME_ID) : " from Snapshot with ID: " + getResourceUuid(ApiConstants.SNAPSHOT_ID))); VirtualMachineTemplate template = _templateService.createPrivateTemplate(this); if (template != null) { @@ -318,7 +318,7 @@ public void execute() { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create private template"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create private Template"); } } @@ -354,14 +354,12 @@ private void ensureAccessCheck(Account account) { private Long findAccountIdToUse(Account callingAccount) { Long accountIdToUse = null; try { - accountIdToUse = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + accountIdToUse = _accountService.finalizeAccountId(accountName, domainId, projectId, true); } catch (InvalidParameterValueException | PermissionDeniedException ex) { - if (logger.isDebugEnabled()) { - logger.debug(String.format("An exception occurred while finalizing account id with accountName, domainId and projectId" + - "using callingAccountId=%s", callingAccount.getUuid()), ex); - } - logger.warn("Unable to find accountId associated with accountName=" + accountName + " and domainId=" - + domainId + " or projectId=" + projectId + ", using callingAccountId=" + callingAccount.getUuid()); + logger.error("Unable to find accountId associated with accountName={} and domainId={} or projectId={}" + + ", using callingAccountId={}", accountName, domainId, projectId, callingAccount.getUuid()); + logger.debug("An exception occurred while finalizing account id with accountName, domainId and projectId" + + "using callingAccountId={}", callingAccount.getUuid(), ex); } return accountIdToUse != null ? accountIdToUse : callingAccount.getAccountId(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/DeleteTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/DeleteTemplateCmd.java index 5e9bf317fe1d..3c7b1e2708b8 100755 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/DeleteTemplateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/DeleteTemplateCmd.java @@ -37,7 +37,7 @@ @APICommand(name = "deleteTemplate", responseObject = SuccessResponse.class, - description = "Deletes a template from the system. All virtual machines using the deleted template will not be affected.", + description = "Deletes a Template from the system. All Instances using the deleted Template will not be affected.", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DeleteTemplateCmd extends BaseAsyncCmd { @@ -45,13 +45,13 @@ public class DeleteTemplateCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TemplateResponse.class, required = true, description = "the ID of the template") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TemplateResponse.class, required = true, description = "The ID of the Template") private Long id; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the ID of zone of the template") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The ID of zone of the Template") private Long zoneId; - @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force delete a template.", since = "4.9+") + @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force delete a Template.", since = "4.9+") private Boolean forced; @Parameter(name = ApiConstants.IS_SYSTEM, type = CommandType.BOOLEAN, required = false, description = "Necessary if the template's type is system.", since = "4.20.0", authorized = {RoleType.Admin}) @@ -98,7 +98,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Deleting template " + this._uuidMgr.getUuid(VirtualMachineTemplate.class, getId()); + return "Deleting Template with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -113,13 +113,13 @@ public Long getApiResourceId() { @Override public void execute() { - CallContext.current().setEventDetails("Template Id: " + this._uuidMgr.getUuid(VirtualMachineTemplate.class, getId())); + CallContext.current().setEventDetails("Template ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _templateService.deleteTemplate(this); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete template"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete Template"); } } } 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 22f59351e9ab..d3f039ce38de 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 @@ -28,13 +28,12 @@ import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; -import com.cloud.dc.DataCenter; import com.cloud.event.EventTypes; import com.cloud.exception.InternalErrorException; import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.Account; -@APICommand(name = "extractTemplate", description = "Extracts a template", responseObject = ExtractResponse.class, +@APICommand(name = "extractTemplate", description = "Extracts a Template", responseObject = ExtractResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ExtractTemplateCmd extends BaseAsyncCmd { @@ -43,20 +42,20 @@ public class ExtractTemplateCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TemplateResponse.class, required = true, description = "the ID of the template") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TemplateResponse.class, required = true, description = "The ID of the Template") private Long id; - @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = false, length = 2048, description = "the url to which the ISO would be extracted") + @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = false, length = 2048, description = "The url to which the ISO would be extracted") private String url; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = false, - description = "the ID of the zone where the ISO is originally located") + description = "The ID of the zone where the ISO is originally located") private Long zoneId; - @Parameter(name = ApiConstants.MODE, type = CommandType.STRING, required = true, description = "the mode of extraction - HTTP_DOWNLOAD or FTP_UPLOAD") + @Parameter(name = ApiConstants.MODE, type = CommandType.STRING, required = true, description = "The mode of extraction - HTTP_DOWNLOAD or FTP_UPLOAD") private String mode; ///////////////////////////////////////////////////// @@ -101,15 +100,14 @@ public String getEventType() { @Override public String getEventDescription() { - String templateId = this._uuidMgr.getUuid(VirtualMachineTemplate.class, getId()); - String baseDescription = String.format("Extracting template: %s", templateId); + String description = "Extracting Template with ID: " + getResourceUuid(ApiConstants.ID); Long zoneId = getZoneId(); - if (zoneId == null) { - return baseDescription; + if (zoneId != null) { + description += "from zone with ID: " + getResourceUuid(ApiConstants.ZONE_ID); } - return String.format("%s from zone: %s", baseDescription, this._uuidMgr.getUuid(DataCenter.class, zoneId)); + return description; } @Override @@ -133,7 +131,7 @@ public void execute() { response.setObjectName("template"); this.setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to extract template"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to extract Template"); } } catch (InternalErrorException ex) { logger.warn("Exception: ", ex); 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 330224a60552..e6e178baada6 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 @@ -38,7 +38,7 @@ import com.cloud.exception.ResourceAllocationException; -@APICommand(name = "getUploadParamsForTemplate", description = "upload an existing template into the CloudStack cloud. ", +@APICommand(name = "getUploadParamsForTemplate", description = "Upload an existing Template into the CloudStack cloud. ", responseObject = GetUploadParamsResponse.class, since = "4.6.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -46,18 +46,18 @@ public class GetUploadParamsForTemplateCmd extends AbstractGetUploadParamsCmd { private static final String s_name = "postuploadtemplateresponse"; - @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "the display text of the template. This is usually used for display purposes.", length = 4096) + @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "The display text of the Template. This is usually used for display purposes.", length = 4096) private String displayText; - @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, required = true, description = "the target hypervisor for the template") + @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, required = true, description = "The target hypervisor for the Template") private String hypervisor; @Parameter(name = ApiConstants.OS_TYPE_ID, type = CommandType.UUID, entityType = GuestOSResponse.class, required = false, - description = "the ID of the OS Type that best represents the OS of this template. Not required for VMware as the guest OS is obtained from the OVF file.") + description = "The ID of the OS Type that best represents the OS of this Template. Not required for VMware as the guest OS is obtained from the OVF file.") private Long osTypeId; @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, - description = "the CPU arch of the template. Valid options are: x86_64, aarch64", + description = "the CPU arch of the template. Valid options are: x86_64, aarch64, s390x", since = "4.20") private String arch; @@ -67,36 +67,36 @@ public class GetUploadParamsForTemplateCmd extends AbstractGetUploadParamsCmd { @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "Template details in key/value pairs.") private Map details; - @Parameter(name = ApiConstants.IS_DYNAMICALLY_SCALABLE, type = CommandType.BOOLEAN, description = "true if template contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory") + @Parameter(name = ApiConstants.IS_DYNAMICALLY_SCALABLE, type = CommandType.BOOLEAN, description = "True if Template contains XS/VMWare tools in order to support dynamic scaling of Instance CPU/memory") private Boolean isDynamicallyScalable; - @Parameter(name = ApiConstants.IS_EXTRACTABLE, type = CommandType.BOOLEAN, description = "true if the template or its derivatives are extractable; default is false") + @Parameter(name = ApiConstants.IS_EXTRACTABLE, type = CommandType.BOOLEAN, description = "True if the Template or its derivatives are extractable; default is false") private Boolean extractable; - @Parameter(name = ApiConstants.IS_FEATURED, type = CommandType.BOOLEAN, description = "true if this template is a featured template, false otherwise") + @Parameter(name = ApiConstants.IS_FEATURED, type = CommandType.BOOLEAN, description = "True if this Template is a featured Template, false otherwise") private Boolean featured; - @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "true if the template is available to all accounts; default is true") + @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "True if the Template is available to all accounts; default is true") private Boolean publicTemplate; - @Parameter(name = ApiConstants.ROUTING, type = CommandType.BOOLEAN, description = "true if the template type is routing i.e., if template is used to deploy router") + @Parameter(name = ApiConstants.ROUTING, type = CommandType.BOOLEAN, description = "True if the Template type is routing i.e., if Template is used to deploy router") private Boolean isRoutingType; - @Parameter(name = ApiConstants.PASSWORD_ENABLED, type = CommandType.BOOLEAN, description = "true if the template supports the password reset feature; default is false") + @Parameter(name = ApiConstants.PASSWORD_ENABLED, type = CommandType.BOOLEAN, description = "True if the Template supports the password reset feature; default is false") private Boolean passwordEnabled; - @Parameter(name = ApiConstants.REQUIRES_HVM, type = CommandType.BOOLEAN, description = "true if this template requires HVM") + @Parameter(name = ApiConstants.REQUIRES_HVM, type = CommandType.BOOLEAN, description = "True if this Template requires HVM") private Boolean requiresHvm; - @Parameter(name = ApiConstants.SSHKEY_ENABLED, type = CommandType.BOOLEAN, description = "true if the template supports the sshkey upload feature; default is false") + @Parameter(name = ApiConstants.SSHKEY_ENABLED, type = CommandType.BOOLEAN, description = "True if the Template supports the SSHkey upload feature; default is false") private Boolean sshKeyEnabled; - @Parameter(name = ApiConstants.TEMPLATE_TAG, type = CommandType.STRING, description = "the tag for this template.") + @Parameter(name = ApiConstants.TEMPLATE_TAG, type = CommandType.STRING, description = "The tag for this Template.") private String templateTag; @Parameter(name=ApiConstants.DEPLOY_AS_IS, type = CommandType.BOOLEAN, - description = "(VMware only) true if VM deployments should preserve all the configurations defined for this template", since = "4.15.1") + description = "(VMware only) true if Instance deployments should preserve all the configurations defined for this Template", since = "4.15.1") private Boolean deployAsIs; @Parameter(name=ApiConstants.FOR_CKS, @@ -104,6 +104,11 @@ public class GetUploadParamsForTemplateCmd extends AbstractGetUploadParamsCmd { 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.22.0") + private String templateType; + public String getDisplayText() { return StringUtils.isBlank(displayText) ? getName() : displayText; } @@ -165,7 +170,7 @@ public Boolean isSshKeyEnabled() { } public String getTemplateTag() { - return templateTag; + return StringUtils.isBlank(templateTag) ? null : templateTag; } public boolean isDeployAsIs() { @@ -181,6 +186,10 @@ public CPU.CPUArch getArch() { return CPU.CPUArch.fromType(arch); } + public String getTemplateType() { + return templateType; + } + @Override public void execute() throws ServerApiException { validateRequest(); @@ -189,14 +198,14 @@ public void execute() throws ServerApiException { response.setResponseName(getCommandName()); setResponseObject(response); } catch (ResourceAllocationException | MalformedURLException e) { - logger.error("exception while registering template", e); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "exception while registering template: " + e.getMessage()); + logger.error("Exception while registering Template", e); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Exception while registering Template: " + e.getMessage()); } } private void validateRequest() { if (getZoneId() <= 0) { - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "invalid zoneid"); + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid zoneid"); } if (!isDeployAsIs() && osTypeId == null) { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Missing parameter ostypeid"); @@ -214,7 +223,7 @@ public String getCommandName() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(getAccountName(), getDomainId(), getProjectId(), true); + Long accountId = _accountService.finalizeAccountId(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/template/ListTemplatePermissionsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatePermissionsCmd.java index 6d544df41871..7e7efcf87cf4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatePermissionsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatePermissionsCmd.java @@ -1,4 +1,4 @@ -// Licensedname = "listTemplatePermissions", to the Apache Software Foundation (ASF) under one +// 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 @@ -26,7 +26,7 @@ import com.cloud.storage.Storage.ImageFormat; import com.cloud.template.VirtualMachineTemplate; -@APICommand(name = "listTemplatePermissions", description = "List template visibility and all accounts that have permissions to view this template.", responseObject = TemplatePermissionsResponse.class, responseView = ResponseView.Restricted, +@APICommand(name = "listTemplatePermissions", description = "List Template visibility and all accounts that have permissions to view this Template.", responseObject = TemplatePermissionsResponse.class, responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListTemplatePermissionsCmd extends BaseListTemplateOrIsoPermissionsCmd implements UserCmd { 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 223ac57b11f8..0b52413aaf19 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 @@ -44,7 +44,7 @@ import com.cloud.template.VirtualMachineTemplate.TemplateFilter; import com.cloud.user.Account; -@APICommand(name = "listTemplates", description = "List all public, private, and privileged templates.", responseObject = TemplateResponse.class, entityType = {VirtualMachineTemplate.class}, responseView = ResponseView.Restricted, +@APICommand(name = "listTemplates", description = "List all public, private, and privileged Templates.", responseObject = TemplateResponse.class, entityType = {VirtualMachineTemplate.class}, responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements UserCmd { @@ -54,47 +54,47 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "the hypervisor for which to restrict the search") + @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "The hypervisor for which to restrict the search") private String hypervisor; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "the template ID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "The Template ID") private Long id; - @Parameter(name=ApiConstants.IDS, type=CommandType.LIST, collectionType=CommandType.UUID, entityType=TemplateResponse.class, description="the IDs of the templates, mutually exclusive with id", since = "4.9") + @Parameter(name=ApiConstants.IDS, type=CommandType.LIST, collectionType=CommandType.UUID, entityType=TemplateResponse.class, description = "The IDs of the Templates, mutually exclusive with id", since = "4.9") private List ids; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the template name") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The Template name") private String templateName; @Parameter(name = ApiConstants.TEMPLATE_FILTER, type = CommandType.STRING, required = true, - description = "possible values are \"featured\", \"self\", \"selfexecutable\",\"sharedexecutable\",\"executable\", and \"community\". " - + "* featured : templates that have been marked as featured and public. " - + "* self : templates that have been registered or created by the calling user. " - + "* selfexecutable : same as self, but only returns templates that can be used to deploy a new VM. " - + "* sharedexecutable : templates ready to be deployed that have been granted to the calling user by another user. " - + "* executable : templates that are owned by the calling user, or public templates, that can be used to deploy a VM. " - + "* community : templates that have been marked as public but not featured. " + "* all : all templates (only usable by admins).") + description = "Possible values are \"featured\", \"self\", \"selfexecutable\",\"sharedexecutable\",\"executable\", and \"community\". " + + "* featured : Templates that have been marked as featured and public. " + + "* self : Templates that have been registered or created by the calling user. " + + "* selfexecutable : same as self, but only returns Templates that can be used to deploy a new Instance. " + + "* sharedexecutable : Templates ready to be deployed that have been granted to the calling user by another user. " + + "* executable : Templates that are owned by the calling user, or public Templates, that can be used to deploy an Instance. " + + "* community : Templates that have been marked as public but not featured. " + "* all : all Templates (only usable by admins).") private String templateFilter; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "list templates by zoneId") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "List Templates by zoneId") private Long zoneId; - @Parameter(name = ApiConstants.SHOW_REMOVED, type = CommandType.BOOLEAN, description = "show removed templates as well") + @Parameter(name = ApiConstants.SHOW_REMOVED, type = CommandType.BOOLEAN, description = "Show removed Templates as well") private Boolean showRemoved; - @Parameter(name = ApiConstants.SHOW_UNIQUE, type = CommandType.BOOLEAN, description = "If set to true, list only unique templates across zones", since = "4.13.2") + @Parameter(name = ApiConstants.SHOW_UNIQUE, type = CommandType.BOOLEAN, description = "If set to true, list only unique Templates across zones", since = "4.13.2") private Boolean showUnique; - @Parameter(name = ApiConstants.PARENT_TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "list datadisk templates by parent template id", since = "4.4") + @Parameter(name = ApiConstants.PARENT_TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "List datadisk Templates by parent Template id", since = "4.4") private Long parentTemplateId; @Parameter(name = ApiConstants.DETAILS, type = CommandType.LIST, collectionType = CommandType.STRING, since = "4.15", - description = "comma separated list of template details requested, value can be a list of [ all, min]") + description = "Comma separated list of Template details requested, value can be a list of [all, min]") private List viewDetails; @Parameter(name = ApiConstants.TEMPLATE_TYPE, type = CommandType.STRING, @@ -112,7 +112,7 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User private Boolean forCks; @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, - description = "the CPU arch of the template. Valid options are: x86_64, aarch64", + description = "the CPU arch of the template. Valid options are: x86_64, aarch64, s390x", since = "4.20") private String arch; @@ -208,7 +208,7 @@ public boolean listInReadyState() { return onlyReady; } - @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, description = "flag to display the resource image for the templates") + @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, description = "Flag to display the resource image for the Templates") private Boolean showIcon; ///////////////////////////////////////////////////// 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 5d5cab219c19..49992ac66611 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 @@ -47,7 +47,7 @@ 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, +@APICommand(name = "registerTemplate", description = "Registers an existing Template into the CloudStack cloud. ", responseObject = TemplateResponse.class, responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class RegisterTemplateCmd extends BaseCmd implements UserCmd { @@ -62,76 +62,76 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd { @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, - description = "The display text of the template, defaults to 'name'.", + description = "The display text of the Template, defaults to 'name'.", length = 4096) private String displayText; @Parameter(name = ApiConstants.FORMAT, type = CommandType.STRING, required = true, - description = "the format for the template. Possible values include QCOW2, RAW, VHD and OVA.") + description = "The format for the Template. Possible values include QCOW2, RAW, VHD and OVA.") private String format; - @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, required = true, description = "the target hypervisor for the template") + @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, required = true, description = "The target hypervisor for the Template") protected String hypervisor; - @Parameter(name = ApiConstants.IS_FEATURED, type = CommandType.BOOLEAN, description = "true if this template is a featured template, false otherwise") + @Parameter(name = ApiConstants.IS_FEATURED, type = CommandType.BOOLEAN, description = "True if this Template is a featured Template, false otherwise") private Boolean featured; - @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "true if the template is available to all accounts; default is true") + @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "True if the Template is available to all accounts; default is true") private Boolean publicTemplate; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the template") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "The name of the Template") private String templateName; @Parameter(name = ApiConstants.OS_TYPE_ID, type = CommandType.UUID, entityType = GuestOSResponse.class, required = false, - description = "the ID of the OS Type that best represents the OS of this template. Not applicable with VMware, as we honour what is defined in the template") + description = "The ID of the OS Type that best represents the OS of this Template. Not applicable with VMware, as we honour what is defined in the Template") private Long osTypeId; @Parameter(name = ApiConstants.PASSWORD_ENABLED, type = CommandType.BOOLEAN, - description = "true if the template supports the password reset feature; default is false") + description = "True if the Template supports the password reset feature; default is false") private Boolean passwordEnabled; - @Parameter(name = ApiConstants.SSHKEY_ENABLED, type = CommandType.BOOLEAN, description = "true if the template supports the sshkey upload feature; default is false") + @Parameter(name = ApiConstants.SSHKEY_ENABLED, type = CommandType.BOOLEAN, description = "True if the Template supports the sshkey upload feature; default is false") private Boolean sshKeyEnabled; - @Parameter(name = ApiConstants.IS_EXTRACTABLE, type = CommandType.BOOLEAN, description = "true if the template or its derivatives are extractable; default is false") + @Parameter(name = ApiConstants.IS_EXTRACTABLE, type = CommandType.BOOLEAN, description = "True if the Template or its derivatives are extractable; default is false") private Boolean extractable; - @Parameter(name = ApiConstants.REQUIRES_HVM, type = CommandType.BOOLEAN, description = "true if this template requires HVM") + @Parameter(name = ApiConstants.REQUIRES_HVM, type = CommandType.BOOLEAN, description = "True if this Template requires HVM") private Boolean requiresHvm; @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, length = 2048, - description = "the URL of where the template is hosted. Possible URL include http:// and https://") + description = "The URL of where the Template is hosted. Possible URL include http:// and https://") private String url; @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.UUID, entityType = ZoneResponse.class, - required=false, description="the ID of the zone the template is to be hosted on") + required=false, description = "The ID of the zone the Template is to be hosted on") protected Long zoneId; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "an optional domainId. If the account parameter is used, domainId must also be used.") + description = "An optional domainId. If the account parameter is used, domainId must also be used.") private Long domainId; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional accountName. Must be used with domainId.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "An optional accountName. Must be used with domainId.") private String accountName; - @Parameter(name = ApiConstants.CHECKSUM, type = CommandType.STRING, description = "the checksum value of this template. " + ApiConstants.CHECKSUM_PARAMETER_PREFIX_DESCRIPTION) + @Parameter(name = ApiConstants.CHECKSUM, type = CommandType.STRING, description = "The checksum value of this Template. " + ApiConstants.CHECKSUM_PARAMETER_PREFIX_DESCRIPTION) private String checksum; - @Parameter(name = ApiConstants.TEMPLATE_TAG, type = CommandType.STRING, description = "the tag for this template.") + @Parameter(name = ApiConstants.TEMPLATE_TAG, type = CommandType.STRING, description = "The tag for this Template.") private String templateTag; - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Register template for the project") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Register Template for the project") private Long projectId; @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, @@ -140,11 +140,11 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd { @Parameter(name = ApiConstants.IS_DYNAMICALLY_SCALABLE, type = CommandType.BOOLEAN, - description = "true if template contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory") + description = "True if Template contains XS/VMWare tools in order to support dynamic scaling of Instance cpu/memory") protected Boolean isDynamicallyScalable; @Deprecated - @Parameter(name = ApiConstants.ROUTING, type = CommandType.BOOLEAN, description = "true if the template type is routing i.e., if template is used to deploy router") + @Parameter(name = ApiConstants.ROUTING, type = CommandType.BOOLEAN, description = "True if the Template type is routing i.e., if Template is used to deploy router") protected Boolean isRoutingType; @Parameter(name=ApiConstants.ZONE_ID_LIST, @@ -152,21 +152,21 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd { collectionType = CommandType.UUID, entityType = ZoneResponse.class, required=false, - description="A list of zone ids where the template will be hosted. Use this parameter if the template needs " + - "to be registered to multiple zones in one go. Use zoneid if the template " + + description = "A list of zone IDs where the Template will be hosted. Use this parameter if the Template needs " + + "to be registered to multiple zones in one go. Use zoneid if the Template " + "needs to be registered to only one zone." + - "Passing only -1 to this will cause the template to be registered as a cross " + - "zone template and will be copied to all zones. ") + "Passing only -1 to this will cause the Template to be registered as a cross " + + "zone Template and will be copied to all zones. ") protected List zoneIds; @Parameter(name=ApiConstants.DIRECT_DOWNLOAD, type = CommandType.BOOLEAN, - description = "true if template should bypass Secondary Storage and be downloaded to Primary Storage on deployment") + description = "True if Template should bypass Secondary Storage and be downloaded to Primary Storage on deployment") private Boolean directDownload; @Parameter(name=ApiConstants.DEPLOY_AS_IS, type = CommandType.BOOLEAN, - description = "(VMware only) true if VM deployments should preserve all the configurations defined for this template", since = "4.15.1") + description = "(VMware only) true if Instance deployments should preserve all the configurations defined for this Template", since = "4.15.1") protected Boolean deployAsIs; @Parameter(name=ApiConstants.FOR_CKS, @@ -180,7 +180,7 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd { private String templateType; @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, - description = "the CPU arch of the template. Valid options are: x86_64, aarch64", + description = "the CPU arch of the template. Valid options are: x86_64, aarch64, s390x", since = "4.20") private String arch; @@ -279,7 +279,7 @@ public String getChecksum() { } public String getTemplateTag() { - return templateTag; + return StringUtils.isBlank(templateTag) ? null : templateTag; } public Map getDetails() { @@ -344,7 +344,7 @@ public ApiCommandResourceType getInstanceType() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } @@ -366,7 +366,7 @@ public void execute() throws ResourceAllocationException { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to register template"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to register Template"); } } catch (URISyntaxException ex1) { logger.info(ex1); @@ -393,7 +393,7 @@ protected void validateParameters() { .isFunctionalitySupported(Hypervisor.HypervisorType.Functionality.DirectDownloadTemplate) || getHypervisor().equalsIgnoreCase(customHypervisor))) { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, String.format("Parameter directdownload " + - "is only allowed for KVM or %s templates", customHypervisor)); + "is only allowed for KVM or %s Templates", customHypervisor)); } if (!isDeployAsIs() && osTypeId == null) { 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 20849d1ba6c5..56d50285692d 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 @@ -30,7 +30,7 @@ import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.Account; -@APICommand(name = "updateTemplate", description = "Updates attributes of a template.", responseObject = TemplateResponse.class, responseView = ResponseView.Restricted, +@APICommand(name = "updateTemplate", description = "Updates attributes of a Template.", responseObject = TemplateResponse.class, responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class UpdateTemplateCmd extends BaseUpdateTemplateOrIsoCmd implements UserCmd { private static final String s_name = "updatetemplateresponse"; @@ -40,7 +40,7 @@ public class UpdateTemplateCmd extends BaseUpdateTemplateOrIsoCmd implements Use ///////////////////////////////////////////////////// @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).") + description = "The type of the Template. Valid options are: USER/VNF (for all users) and SYSTEM/ROUTING/BUILTIN (for admins only).") private String templateType; @Parameter(name = ApiConstants.TEMPLATE_TAG, type = CommandType.STRING, description = "the tag for this template.", since = "4.20.0") @@ -115,7 +115,7 @@ public void execute() { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update template"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update Template"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/UpdateTemplatePermissionsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/UpdateTemplatePermissionsCmd.java index de8f09a64005..9c408238fe21 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/UpdateTemplatePermissionsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/UpdateTemplatePermissionsCmd.java @@ -24,9 +24,9 @@ import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.Account; -@APICommand(name = "updateTemplatePermissions", responseObject = SuccessResponse.class, description = "Updates a template visibility permissions. " - + "A public template is visible to all accounts within the same domain. " + "A private template is visible only to the owner of the template. " - + "A privileged template is a private template with account permissions added. " + "Only accounts specified under the template permissions are visible to them.", entityType = {VirtualMachineTemplate.class}, +@APICommand(name = "updateTemplatePermissions", responseObject = SuccessResponse.class, description = "Updates a Template visibility permissions. " + + "A public Template is visible to all accounts within the same domain. " + "A private Template is visible only to the owner of the Template. " + + "A privileged Template is a private Template with account permissions added. " + "Only accounts specified under the Template permissions are visible to them.", entityType = {VirtualMachineTemplate.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class UpdateTemplatePermissionsCmd extends BaseUpdateTemplateOrIsoPermissionsCmd { @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/DeleteUserDataCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/DeleteUserDataCmd.java index a1d1afc7b057..f6d29e5dc40c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/DeleteUserDataCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/DeleteUserDataCmd.java @@ -43,20 +43,20 @@ public class DeleteUserDataCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, required = true, entityType = UserDataResponse.class, description = "the ID of the Userdata") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, required = true, entityType = UserDataResponse.class, description = "The ID of the Userdata") private Long id; //Owner information - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the userdata. Must be used with domainId.") + @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.") + 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") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "An optional project for the userdata") private Long projectId; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/LinkUserDataToTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/LinkUserDataToTemplateCmd.java index e322de00bb1b..c8c6d17d4162 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/LinkUserDataToTemplateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/LinkUserDataToTemplateCmd.java @@ -34,7 +34,7 @@ import com.cloud.user.UserData; import com.cloud.utils.exception.CloudRuntimeException; -@APICommand(name = "linkUserDataToTemplate", description = "Link or unlink a userdata to a template.", responseObject = TemplateResponse.class, responseView = ResponseObject.ResponseView.Restricted, +@APICommand(name = "linkUserDataToTemplate", description = "Link or unlink a userdata to a Template.", responseObject = TemplateResponse.class, responseView = ResponseObject.ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.18.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) public class LinkUserDataToTemplateCmd extends BaseCmd implements AdminCmd { @@ -47,24 +47,24 @@ public class LinkUserDataToTemplateCmd extends BaseCmd implements AdminCmd { @Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, - description = "the ID of the template for the virtual machine") + description = "The ID of the Template for the Instance") private Long templateId; @Parameter(name = ApiConstants.ISO_ID, type = CommandType.UUID, entityType = TemplateResponse.class, - description = "the ID of the ISO for the virtual machine") + description = "The ID of the ISO for the Instance") private Long isoId; @Parameter(name = ApiConstants.USER_DATA_ID, type = CommandType.UUID, entityType = UserDataResponse.class, - description = "the ID of the userdata that has to be linked to template/ISO. If not provided existing userdata will be unlinked from the template/ISO") + description = "The ID of the userdata that has to be linked to Template/ISO. If not provided existing userdata will be unlinked from the Template/ISO") private Long userdataId; @Parameter(name = ApiConstants.USER_DATA_POLICY, type = CommandType.STRING, - description = "an optional override policy of the userdata. Possible values are - ALLOWOVERRIDE, APPEND, DENYOVERRIDE. Default policy is allowoverride") + description = "An optional override policy of the userdata. Possible values are - ALLOWOVERRIDE, APPEND, DENYOVERRIDE. Default policy is allowoverride") private String userdataPolicy; ///////////////////////////////////////////////////// @@ -96,7 +96,7 @@ public void execute() { try { result = _templateService.linkUserDataToTemplate(this); } catch (Exception e) { - throw new CloudRuntimeException(String.format("Failed to link userdata to template, due to: %s", e.getLocalizedMessage()), e); + throw new CloudRuntimeException(String.format("Failed to link userdata to Template, due to: %s", e.getLocalizedMessage()), e); } if (result != null) { TemplateResponse response = _responseGenerator.createTemplateUpdateResponse(getResponseView(), result); @@ -109,7 +109,7 @@ public void execute() { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to link userdata to template"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to link userdata to Template"); } } 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 16bf1e5c1e44..693342305242 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 @@ -38,7 +38,7 @@ public class ListUserDataCmd extends BaseListProjectAndAccountResourcesCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserDataResponse.class, description = "the ID of the Userdata") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserDataResponse.class, description = "The ID of the Userdata") private Long id; @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Userdata name to look for") 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 index eb80da3be05b..3f1de41eab89 100644 --- 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 @@ -67,7 +67,7 @@ public void execute() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(getAccountName(), getDomainId(), getProjectId(), true); + Long accountId = _accountService.finalizeAccountId(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/userdata/RegisterUserDataCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterUserDataCmd.java index 185c9c05b1ca..d99f2fd066d6 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 @@ -46,9 +46,20 @@ public class RegisterUserDataCmd extends BaseRegisterUserDataCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.USER_DATA, type = CommandType.STRING, required = true, description = "User data content", length = 1048576) + @Parameter(name = ApiConstants.USER_DATA, + type = CommandType.STRING, + required = true, + description = "Base64 encoded User Data 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 32KB of data after base64 encoding, " + + "which can be increased upto 1MB using the vm.userdata.max.length setting", + length = 1048576) protected String userData; + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + public String getUserData() { return userData; } @@ -59,7 +70,7 @@ public String getUserData() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(getAccountName(), getDomainId(), getProjectId(), true); + Long accountId = _accountService.finalizeAccountId(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/AddIpToVmNicCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/AddIpToVmNicCmd.java index e76a75ae398a..9d96f104885e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/AddIpToVmNicCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/AddIpToVmNicCmd.java @@ -50,12 +50,15 @@ public class AddIpToVmNicCmd extends BaseAsyncCreateCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NIC_ID, type = CommandType.UUID, entityType = NicResponse.class, required = true, description = "the ID of the nic to which you want to assign private IP") + @Parameter(name = ApiConstants.NIC_ID, type = CommandType.UUID, entityType = NicResponse.class, required = true, description = "The ID of the NIC to which you want to assign private IP") private Long nicId; @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, required = false, description = "Secondary IP Address") private String ipAddr; + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, required = false, description = "Description of the secondary IP address", length = 2048) + private String description; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -67,7 +70,7 @@ public String getEntityTable() { private long getNetworkId() { Nic nic = _entityMgr.findById(Nic.class, nicId); if (nic == null) { - throw new InvalidParameterValueException("Can't find network id for specified nic"); + throw new InvalidParameterValueException("Can't find Network ID for specified NIC"); } return nic.getNetworkId(); } @@ -89,7 +92,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "associating ip to nic id=" + this._uuidMgr.getUuid(Nic.class, getNicId()) + " belonging to network id=" + this._uuidMgr.getUuid(Network.class, getNetworkId()); + return "Associating secondary IP address to NIC with ID: " + getResourceUuid(ApiConstants.NIC_ID) + " belonging to Network with ID: " + this._uuidMgr.getUuid(Network.class, getNetworkId()); } ///////////////////////////////////////////////////// @@ -108,23 +111,23 @@ public static String getResultObjectName() { @Override public void execute() throws ResourceUnavailableException, ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException { - CallContext.current().setEventDetails("Nic Id: " + this._uuidMgr.getUuid(Nic.class, getNicId())); + CallContext.current().setEventDetails("Nic ID: " + getResourceUuid(ApiConstants.NIC_ID)); NicSecondaryIp result = _entityMgr.findById(NicSecondaryIp.class, getEntityId()); if (result != null) { - CallContext.current().setEventDetails("secondary Ip Id: " + getEntityUuid()); + CallContext.current().setEventDetails("Secondary IP address ID: " + getEntityUuid()); boolean success = false; success = _networkService.configureNicSecondaryIp(result, isZoneSGEnabled()); if (success == false) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to set security group rules for the secondary ip"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to set security group rules for the secondary IP"); } NicSecondaryIpResponse response = _responseGenerator.createSecondaryIPToNicResponse(result); response.setResponseName(getCommandName()); this.setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to assign secondary ip to nic"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to assign secondary IP to NIC"); } } @@ -142,7 +145,7 @@ public ApiCommandResourceType getApiResourceType() { public long getEntityOwnerId() { Nic nic = _entityMgr.findById(Nic.class, nicId); if (nic == null) { - throw new InvalidParameterValueException("Can't find nic for id specified"); + throw new InvalidParameterValueException("Can't find NIC for id specified"); } long vmId = nic.getInstanceId(); VirtualMachine vm = _entityMgr.findById(VirtualMachine.class, vmId); @@ -160,17 +163,17 @@ public void create() throws ResourceAllocationException { } try { - result = _networkService.allocateSecondaryGuestIP(getNicId(), requestedIpPair); + result = _networkService.allocateSecondaryGuestIP(getNicId(), requestedIpPair, description); if (result != null) { setEntityId(result.getId()); setEntityUuid(result.getUuid()); } } catch (InsufficientAddressCapacityException e) { - throw new InvalidParameterValueException("Allocating guest ip for nic failed : " + e.getMessage()); + throw new InvalidParameterValueException("Allocating guest IP for NIC failed : " + e.getMessage()); } if (result == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to assign secondary ip to nic"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to assign secondary IP to NIC"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/AddNicToVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/AddNicToVMCmd.java index ecd066d98cd5..f6ef955956f6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/AddNicToVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/AddNicToVMCmd.java @@ -40,14 +40,13 @@ import com.cloud.event.EventTypes; import com.cloud.exception.InvalidParameterValueException; -import com.cloud.network.Network; import com.cloud.user.Account; import com.cloud.uservm.UserVm; import com.cloud.utils.net.Dhcp; import com.cloud.utils.net.NetUtils; import com.cloud.vm.VirtualMachine; -@APICommand(name = "addNicToVirtualMachine", description = "Adds VM to specified network by creating a NIC", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, +@APICommand(name = "addNicToVirtualMachine", description = "Adds Instance to specified Network by creating a NIC", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class AddNicToVMCmd extends BaseAsyncCmd implements UserCmd { private static final String s_name = "addnictovirtualmachineresponse"; @@ -57,19 +56,19 @@ public class AddNicToVMCmd extends BaseAsyncCmd implements UserCmd { ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) @Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.UUID, entityType=UserVmResponse.class, - required=true, description="Virtual Machine ID") + required=true, description = "Instance ID") private Long vmId; @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "Network ID") private Long netId; - @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, description = "IP Address for the new network") + @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, description = "IP Address for the new Network") private String ipaddr; - @Parameter(name = ApiConstants.MAC_ADDRESS, type = CommandType.STRING, description = "Mac Address for the new network") + @Parameter(name = ApiConstants.MAC_ADDRESS, type = CommandType.STRING, description = "Mac Address for the new Network") private String macaddr; - @Parameter(name = ApiConstants.DHCP_OPTIONS, type = CommandType.MAP, description = "DHCP options which are passed to the nic" + @Parameter(name = ApiConstants.DHCP_OPTIONS, type = CommandType.MAP, description = "DHCP options which are passed to the NIC" + " Example: dhcpoptions[0].dhcp:114=url&dhcpoptions[0].dhcp:66=www.test.com") private Map dhcpOptions; @@ -101,6 +100,26 @@ public String getMacAddress() { return NetUtils.standardizeMacAddress(macaddr); } + public void setVmId(Long vmId) { + this.vmId = vmId; + } + + public void setNetworkId(Long netId) { + this.netId = netId; + } + + public void setIpaddr(String ipaddr) { + this.ipaddr = ipaddr; + } + + public void setMacAddress(String macaddr) { + this.macaddr = macaddr; + } + + public void setDhcpOptions(Map dhcpOptions) { + this.dhcpOptions = dhcpOptions; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -121,7 +140,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Adding network " + this._uuidMgr.getUuid(Network.class, getNetworkId()) + " to user vm: " + this._uuidMgr.getUuid(VirtualMachine.class, getVmId()); + return "Adding NIC on Network " + getResourceUuid(ApiConstants.NETWORK_ID) + " to User Instance: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID); } @Override @@ -167,7 +186,7 @@ public ApiCommandResourceType getApiResourceType() { @Override public void execute() { - CallContext.current().setEventDetails("Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getVmId()) + " Network Id: " + this._uuidMgr.getUuid(Network.class, getNetworkId())); + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID) + " Network ID: " + getResourceUuid(ApiConstants.NETWORK_ID)); UserVm result = _userVmService.addNicToVirtualMachine(this); ArrayList dc = new ArrayList(); dc.add(VMDetails.valueOf("nics")); @@ -177,7 +196,7 @@ public void execute() { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add NIC to vm. Refer to server logs for details."); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add NIC to Instance. Refer to server logs for details."); } } } 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 index ecbde47692f2..38e9921853a3 100644 --- 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 @@ -40,12 +40,14 @@ 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.KMSKeyResponse; 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.kms.KMSKey; import org.apache.cloudstack.vm.lease.VMLeaseManager; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; @@ -61,10 +63,10 @@ import com.cloud.network.Network.IpAddresses; import com.cloud.offering.DiskOffering; import com.cloud.template.VirtualMachineTemplate; +import com.cloud.utils.net.Dhcp; 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 { @@ -75,13 +77,13 @@ public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd impleme ///////////////////////////////////////////////////// @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "availability zone for the virtual machine") - private Long zoneId; + protected Long zoneId; @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "host name for the virtual machine", validations = {ApiArgValidator.RFCComplianceDomainName}) - private String name; + protected String name; @Parameter(name = ApiConstants.DISPLAY_NAME, type = CommandType.STRING, description = "an optional user generated name for the virtual machine") - private String displayName; + protected 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") @@ -89,21 +91,21 @@ public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd impleme //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; + protected 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; + protected 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; + protected 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; + protected 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; + protected 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; @@ -126,11 +128,19 @@ public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd impleme since = "4.4") private Long rootdisksize; + @ACL + @Parameter(name = ApiConstants.ROOT_DISK_KMS_KEY_ID, + type = CommandType.UUID, + entityType = KMSKeyResponse.class, + description = "ID of the KMS Key to use for root disk encryption", + since = "4.23.0") + private Long rootDiskKmsKeyId; + @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") + " Example: datadisksdetails[0].diskofferingid=a2a73a84-19db-4852-8930-dfddef053341&datadisksdetails[0].size=10&datadisksdetails[0].miniops=100&datadisksdetails[0].maxiops=200&datadisksdetails[0].kmskeyid=") private Map dataDisksDetails; @Parameter(name = ApiConstants.GROUP, type = CommandType.STRING, description = "an optional group for the virtual machine") @@ -138,7 +148,7 @@ public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd impleme @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; + protected 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. " + @@ -147,10 +157,10 @@ public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd impleme "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; + protected 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; + protected 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; @@ -160,7 +170,7 @@ public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd impleme 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; + protected 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; @@ -168,7 +178,7 @@ public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd impleme @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; + protected 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." @@ -188,11 +198,11 @@ public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd impleme @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.KEYBOARD, type = CommandType.STRING, description = "an optional keyboard device type for the virtual machine. valid value can be one of de,de-ch,es,es-latam,fi,fr,fr-be,fr-ch,is,it,jp,nl-be,no,pt,uk,us") + protected String keyboard; @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Deploy vm for the project") - private Long projectId; + protected 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; @@ -200,7 +210,7 @@ public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd impleme @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; + protected 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." @@ -208,10 +218,10 @@ public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd impleme 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; + protected 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; + protected 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; @@ -225,7 +235,7 @@ public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd impleme 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; + protected 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; @@ -300,6 +310,10 @@ public Long getDiskOfferingId() { return diskOfferingId; } + public Long getRootDiskKmsKeyId() { + return rootDiskKmsKeyId; + } + public String getDeploymentPlanner() { return deploymentPlanner; } @@ -416,9 +430,7 @@ public Map getVmNetworkMap() { nic = null; } String networkUuid = entry.get(VmDetailConstants.NETWORK); - if (logger.isTraceEnabled()) { - logger.trace(String.format("nic, '%s', goes on net, '%s'", nic, networkUuid)); - } + logger.trace("Checking if NIC '{}' can be mapped on network '{}'", 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)); } @@ -583,7 +595,19 @@ public List getDataDiskInfoList() { 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); + + // Extract KMS key ID if provided + Long kmsKeyId = null; + String kmsKeyUuid = dataDisk.get(ApiConstants.KMS_KEY_ID); + if (kmsKeyUuid != null) { + KMSKey kmsKey = _entityMgr.findByUuid(org.apache.cloudstack.kms.KMSKey.class, kmsKeyUuid); + if (kmsKey == null) { + throw new InvalidParameterValueException("Unable to find KMS key " + kmsKeyUuid); + } + kmsKeyId = kmsKey.getId(); + } + + VmDiskInfo vmDiskInfo = new VmDiskInfo(diskOffering, size, minIops, maxIops, deviceId, kmsKeyId); vmDiskInfoList.add(vmDiskInfo); } this.dataDiskInfoList = vmDiskInfoList; @@ -800,6 +824,11 @@ public IoDriverPolicy getIoDriverPolicy() { } return null; } + + public String getInstanceType() { + return null; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -810,7 +839,7 @@ public static String getResultObjectName() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } @@ -830,15 +859,15 @@ public String getCreateEventType() { @Override public String getCreateEventDescription() { - return "creating Vm"; + return "Creating Instance"; } @Override public String getEventDescription() { if(getStartVm()) { - return "starting Vm. Vm Id: " + getEntityUuid(); + return "Starting Instance with ID: " + getEntityUuid(); } - return "deploying Vm. Vm Id: " + getEntityUuid(); + return "Deploying Instance with ID: " + getEntityUuid(); } @Override 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 index 04e413ed67ab..7743c031006b 100644 --- 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 @@ -36,6 +36,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.uservm.UserVm; import com.cloud.vm.VirtualMachine; +import org.apache.commons.lang3.ObjectUtils; @APICommand(name = "createVMFromBackup", description = "Creates and automatically starts a VM from a backup.", @@ -51,6 +52,7 @@ public class CreateVMFromBackupCmd extends BaseDeployVMCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// + @ACL @Parameter(name = ApiConstants.BACKUP_ID, type = CommandType.UUID, entityType = BackupResponse.class, @@ -69,6 +71,10 @@ public class CreateVMFromBackupCmd extends BaseDeployVMCmd { @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; + @Parameter(name = ApiConstants.QUICK_RESTORE, type = CommandType.BOOLEAN, entityType = BackupResponse.class, description = "Whether to use the quick restore process or not. " + + "Currently this parameter is only supported by the KBOSS provider.", since = "4.23.0") + private Boolean quickRestore; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -89,6 +95,10 @@ public boolean getPreserveIp() { return (preserveIp != null) ? preserveIp : false; } + public Boolean getQuickRestore() { + return ObjectUtils.defaultIfNull(this.quickRestore, false); + } + @Override public void create() { UserVm vm; @@ -137,7 +147,7 @@ public void execute () { 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.info("{}: {}", message.toString(), ex.getLocalizedMessage()); logger.debug(message.toString(), ex); throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, message.toString()); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CreateVMScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CreateVMScheduleCmd.java index 5811eb1abfc5..1fce8caf8e1a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CreateVMScheduleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CreateVMScheduleCmd.java @@ -22,29 +22,32 @@ import com.cloud.vm.VirtualMachine; 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.BaseCmd; import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.ResourceScheduleResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.VMScheduleResponse; -import org.apache.cloudstack.vm.schedule.VMScheduleManager; +import org.apache.cloudstack.schedule.ResourceScheduleManager; import javax.inject.Inject; import java.util.Date; -@APICommand(name = "createVMSchedule", description = "Create VM Schedule", responseObject = VMScheduleResponse.class, +@Deprecated +@APICommand(name = "createVMSchedule", description = "Create Instance Schedule", responseObject = VMScheduleResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.19.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) public class CreateVMScheduleCmd extends BaseCmd { @Inject - VMScheduleManager vmScheduleManager; + ResourceScheduleManager resourceScheduleManager; @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, - description = "ID of the VM for which schedule is to be defined") + description = "ID of the Instance for which schedule is to be defined") private Long vmId; @Parameter(name = ApiConstants.DESCRIPTION, @@ -56,7 +59,7 @@ public class CreateVMScheduleCmd extends BaseCmd { @Parameter(name = ApiConstants.SCHEDULE, type = CommandType.STRING, required = true, - description = "Schedule for action on VM in cron format. e.g. '0 15 10 * *' for 'at 15:00 on 10th day of every month'") + description = "Schedule for action on Instance in cron format. e.g. '0 15 10 * *' for 'at 15:00 on 10th day of every month'") private String schedule; @Parameter(name = ApiConstants.TIMEZONE, @@ -68,32 +71,32 @@ public class CreateVMScheduleCmd extends BaseCmd { @Parameter(name = ApiConstants.ACTION, type = CommandType.STRING, required = true, - description = "Action to take on the VM (start/stop/reboot/force_stop/force_reboot).") + description = "Action to take on the Instance (start/stop/reboot/force_stop/force_reboot).") private String action; @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, required = false, - description = "start date from which the schedule becomes active. Defaults to current date plus 1 minute." - + "Use format \"yyyy-MM-dd hh:mm:ss\")") + description = "Start date from which the schedule becomes active. Defaults to current date plus 1 minute." + + "(Format \"yyyy-MM-dd hh:mm:ss\")") private Date startDate; @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, required = false, - description = "end date after which the schedule becomes inactive" - + "Use format \"yyyy-MM-dd hh:mm:ss\")") + description = "End date after which the schedule becomes inactive" + + "(Format \"yyyy-MM-dd hh:mm:ss\")") private Date endDate; @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, required = false, - description = "Enable VM schedule. Defaults to true") + description = "Enable Instance schedule. Defaults to true") private Boolean enabled; - ///////////////////////////////////////////////////// - /////////////////// Accessors /////////////////////// - ///////////////////////////////////////////////////// + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// public Long getVmId() { return vmId; @@ -130,13 +133,19 @@ public Boolean getEnabled() { return enabled; } - ///////////////////////////////////////////////////// - /////////////// API Implementation/////////////////// - ///////////////////////////////////////////////////// + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// @Override public void execute() { - VMScheduleResponse response = vmScheduleManager.createSchedule(this); + String resourceIdStr = getVmId() != null ? String.valueOf(getVmId()) : null; + + ResourceScheduleResponse scheduleResponse = resourceScheduleManager.createSchedule( + ApiCommandResourceType.VirtualMachine, + resourceIdStr, getDescription(), getSchedule(), getTimeZone(), getAction(), + getStartDate(), getEndDate(), getEnabled(), null); + VMScheduleResponse response = new VMScheduleResponse(scheduleResponse); response.setResponseName(getCommandName()); setResponseObject(response); } @@ -145,7 +154,7 @@ public void execute() { public long getEntityOwnerId() { VirtualMachine vm = _entityMgr.findById(VirtualMachine.class, getVmId()); if (vm == null) { - throw new InvalidParameterValueException(String.format("Unable to find VM by id=%d", getVmId())); + throw new InvalidParameterValueException(String.format("Unable to find Instance by id=%d", getVmId())); } return vm.getAccountId(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeleteVMScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeleteVMScheduleCmd.java index 775a902e0bb9..f40db4ee0487 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeleteVMScheduleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeleteVMScheduleCmd.java @@ -22,6 +22,7 @@ import com.cloud.vm.VirtualMachine; 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; @@ -30,43 +31,45 @@ import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.VMScheduleResponse; -import org.apache.cloudstack.vm.schedule.VMSchedule; -import org.apache.cloudstack.vm.schedule.VMScheduleManager; +import org.apache.cloudstack.schedule.ResourceScheduleManager; import javax.inject.Inject; import java.util.Collections; import java.util.List; -@APICommand(name = "deleteVMSchedule", description = "Delete VM Schedule.", responseObject = SuccessResponse.class, +@Deprecated +@APICommand(name = "deleteVMSchedule", description = "Delete Instance Schedule.", responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.19.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) public class DeleteVMScheduleCmd extends BaseCmd { @Inject - VMScheduleManager vmScheduleManager; + ResourceScheduleManager resourceScheduleManager; @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, - description = "ID of VM") + description = "ID of Instance") private Long vmId; + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VMScheduleResponse.class, required = false, - description = "ID of VM schedule") + description = "ID of Instance schedule") private Long id; + @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = VMScheduleResponse.class, required = false, - description = "IDs of VM schedule") + description = "IDs of Instance schedule") private List ids; - ///////////////////////////////////////////////////// - /////////////////// Accessors /////////////////////// - ///////////////////////////////////////////////////// + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// public Long getId() { return id; @@ -83,21 +86,24 @@ public Long getVmId() { return vmId; } - ///////////////////////////////////////////////////// - /////////////// API Implementation/////////////////// - ///////////////////////////////////////////////////// + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// @Override public void execute() { - long rowsRemoved = vmScheduleManager.removeSchedule(this); + String resourceIdStr = getVmId() != null ? String.valueOf(getVmId()) : null; + long rowsRemoved = resourceScheduleManager.removeSchedule( + ApiCommandResourceType.VirtualMachine, + resourceIdStr, getId(), getIds()); if (rowsRemoved > 0) { final SuccessResponse response = new SuccessResponse(); response.setResponseName(getCommandName()); - response.setObjectName(VMSchedule.class.getSimpleName().toLowerCase()); + response.setObjectName("vmschedule"); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete VM Schedules"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete Instance Schedules"); } } @@ -105,7 +111,7 @@ public void execute() { public long getEntityOwnerId() { VirtualMachine vm = _entityMgr.findById(VirtualMachine.class, getVmId()); if (vm == null) { - throw new InvalidParameterValueException(String.format("Unable to find VM by id=%d", getVmId())); + throw new InvalidParameterValueException(String.format("Unable to find Instance by id=%d", getVmId())); } return vm.getAccountId(); } 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 dc1ca3583cc3..0a943fab118d 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,6 +16,8 @@ // under the License. package org.apache.cloudstack.api.command.user.vm; +import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.stream.Stream; @@ -42,8 +44,8 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VirtualMachine; -@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) +@APICommand(name = "deployVirtualMachine", description = "Creates and automatically starts an Instance based on a service offering, disk offering, and Template.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, + requestHasSensitiveInfo = false) public class DeployVMCmd extends BaseDeployVMCmd { ///////////////////////////////////////////////////// @@ -51,11 +53,11 @@ public class DeployVMCmd extends BaseDeployVMCmd { ///////////////////////////////////////////////////// @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") + @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, required = true, description = "The ID of the Service offering for the Instance") 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") + @Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "The ID of the Template for the Instance") private Long templateId; @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.UUID, entityType = VolumeResponse.class, since = "4.21") @@ -87,11 +89,117 @@ public Long getSnapshotId() { public boolean isVolumeOrSnapshotProvided() { return volumeId != null || snapshotId != null; } + + public boolean isBlankInstance() { + return false; + } + + + + ///////////////////////////////////////////////////// + ////////////////// Setters ////////////////////////// + ///////////////////////////////////////////////////// + public void setZoneId(Long zoneId) { + this.zoneId = zoneId; + } + + public void setName(String name) { + this.name = name; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public void setDomainId(Long domainId) { + this.domainId = domainId; + } + + public void setNetworkIds(List networkIds) { + this.networkIds = networkIds; + } + + public void setBootType(String bootType) { + this.bootType = bootType; + } + + public void setBootMode(String bootMode) { + this.bootMode = bootMode; + } + + public void setHypervisor(String hypervisor) { + this.hypervisor = hypervisor; + } + + public void setUserData(String userData) { + this.userData = userData; + } + + public void setKeyboard(String keyboard) { + this.keyboard = keyboard; + } + + public void setProjectId(Long projectId) { + this.projectId = projectId; + } + + public void setDisplayVm(Boolean displayVm) { + this.displayVm = displayVm; + } + + public void setUserDataId(Long userDataId) { + this.userdataId = userDataId; + } + + public void setAffinityGroupIds(List ids) { + this.affinityGroupIdList = ids; + } + + public void setDetails(Map details) { + this.details = details; + } + + public void setExtraConfig(String extraConfig) { + this.extraConfig = extraConfig; + } + + public void setDynamicScalingEnabled(Boolean dynamicScalingEnabled) { + this.dynamicScalingEnabled = dynamicScalingEnabled; + } + + public void setServiceOfferingId(Long serviceOfferingId) { + this.serviceOfferingId = serviceOfferingId; + } + + public void setTemplateId(Long templateId) { + this.templateId = templateId; + } + + public void setVolumeId(Long volumeId) { + this.volumeId = volumeId; + } + + public void setSnapshotId(Long snapshotId) { + this.snapshotId = snapshotId; + } + + public void setSshKeyPairNames(List sshKeyPairNames) { + this.sshKeyPairNames = sshKeyPairNames; + } + + public void setSecurityGroupList(List securityGroupIdList) { + this.securityGroupIdList = securityGroupIdList; + } + @Override public void execute() { UserVm result; - CallContext.current().setEventDetails("Vm Id: " + getEntityUuid()); + CallContext.current().setEventDetails("Instance ID: " + getEntityUuid()); if (getStartVm()) { try { result = _userVmService.startVirtualMachine(this); @@ -111,12 +219,12 @@ public void execute() { 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.info("{}: {}", message.toString(), ex.getLocalizedMessage()); logger.debug(message.toString(), ex); throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, message.toString()); } } else { - logger.info("VM " + getEntityUuid() + " already created, load UserVm from DB"); + logger.info("Instance {} already created, load UserVm from DB", getEntityUuid()); result = _userVmService.finalizeCreateVirtualMachine(getEntityId()); } @@ -125,13 +233,13 @@ public void execute() { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to deploy vm uuid:"+getEntityUuid()); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to deploy Instance UUID:"+getEntityUuid()); } } @Override public void create() throws ResourceAllocationException { - if (Stream.of(templateId, snapshotId, volumeId).filter(Objects::nonNull).count() != 1) { + if (!isBlankInstance() && 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"); } @@ -142,7 +250,7 @@ public void create() throws ResourceAllocationException { setEntityId(vm.getId()); setEntityUuid(vm.getUuid()); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to deploy vm"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to deploy Instance"); } } catch (InsufficientCapacityException ex) { logger.info(ex); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVnfApplianceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVnfApplianceCmd.java index 4d50dd9c39bf..92ddfd5b2357 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVnfApplianceCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVnfApplianceCmd.java @@ -43,7 +43,7 @@ public class DeployVnfApplianceCmd extends DeployVMCmd implements UserCmd { @Parameter(name = ApiConstants.VNF_CONFIGURE_MANAGEMENT, type = CommandType.BOOLEAN, required = false, - description = "True by default, security group or network rules (source nat and firewall rules) will be configured for VNF management interfaces. False otherwise. " + + description = "False by default, security group or network rules (source nat and firewall rules) will be configured for VNF management interfaces. True otherwise. " + "Network rules are configured if management network is an isolated network or shared network with security groups.") private Boolean vnfConfigureManagement; 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 18a9d2058a62..0a3e510d3d53 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 @@ -41,7 +41,7 @@ import com.cloud.uservm.UserVm; import com.cloud.vm.VirtualMachine; -@APICommand(name = "destroyVirtualMachine", description = "Destroys a virtual machine.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, +@APICommand(name = "destroyVirtualMachine", description = "Destroys an Instance.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class DestroyVMCmd extends BaseAsyncCmd implements UserCmd { @@ -54,12 +54,12 @@ public class DestroyVMCmd extends BaseAsyncCmd implements UserCmd { @ACL(accessType = AccessType.OperateEntry) @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType=UserVmResponse.class, - required=true, description="The ID of the virtual machine") + required=true, description = "The ID of the Instance") private Long id; @Parameter(name = ApiConstants.EXPUNGE, type = CommandType.BOOLEAN, - description = "If true is passed, the vm is expunged immediately. False by default.", + description = "If true is passed, the Instance is expunged immediately. False by default.", since = "4.2.1") private Boolean expunge; @@ -90,10 +90,22 @@ public List getVolumeIds() { return volumeIds; } + public boolean isForced() { + return false; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// + public DestroyVMCmd() { + } + + public DestroyVMCmd(Long id, Boolean expunge) { + this.id = id; + this.expunge = expunge; + } + @Override public String getCommandName() { return s_name; @@ -116,7 +128,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "destroying vm: " + this._uuidMgr.getUuid(VirtualMachine.class, getId()); + return "Destroying Instance with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -131,8 +143,8 @@ public Long getApiResourceId() { @Override public void execute() throws ResourceUnavailableException, ConcurrentOperationException { - CallContext.current().setEventDetails("Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getId())); - UserVm result = _userVmService.destroyVm(this); + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.ID)); + UserVm result = _userVmService.destroyVm(this, true); UserVmResponse response = new UserVmResponse(); if (result != null) { @@ -144,7 +156,7 @@ public void execute() throws ResourceUnavailableException, ConcurrentOperationEx response.setObjectName("virtualmachine"); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to destroy vm"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to destroy Instance"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/GetVMPasswordCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/GetVMPasswordCmd.java index 11534fd43759..d2e6ef9e7428 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/GetVMPasswordCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/GetVMPasswordCmd.java @@ -30,7 +30,7 @@ import com.cloud.uservm.UserVm; import com.cloud.vm.VirtualMachine; -@APICommand(name = "getVMPassword", responseObject = GetVMPasswordResponse.class, description = "Returns an encrypted password for the VM", entityType = {VirtualMachine.class}, +@APICommand(name = "getVMPassword", responseObject = GetVMPasswordResponse.class, description = "Returns an encrypted password for the Instance", entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class GetVMPasswordCmd extends BaseCmd { @@ -40,7 +40,7 @@ public class GetVMPasswordCmd extends BaseCmd { @ACL(accessType = AccessType.OperateEntry) @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType=UserVmResponse.class - , required=true, description="The ID of the virtual machine") + , required=true, description = "The ID of the Instance") private Long id; ///////////////////////////////////////////////////// @@ -59,7 +59,7 @@ public Long getId() { public void execute() { String passwd = _mgr.getVMPassword(this); if (passwd == null || passwd.equals("")) - throw new InvalidParameterValueException("No password for VM with id '" + getId() + "' found."); + throw new InvalidParameterValueException("No password for Instance with ID '" + getId() + "' found."); setResponseObject(new GetVMPasswordResponse(getCommandName(), passwd)); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListNicsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListNicsCmd.java index 0e659fc02a1a..7d2b27e7ac5f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListNicsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListNicsCmd.java @@ -41,7 +41,7 @@ import com.cloud.user.Account; import com.cloud.vm.Nic; -@APICommand(name = "listNics", description = "list the vm nics IP to NIC", responseObject = NicResponse.class, +@APICommand(name = "listNics", description = "List the Instance NICs IP to NIC", responseObject = NicResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListNicsCmd extends BaseListCmd { @@ -49,16 +49,16 @@ public class ListNicsCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NIC_ID, type = CommandType.UUID, entityType = NicResponse.class, required = false, description = "the ID of the nic to list IPs") + @Parameter(name = ApiConstants.NIC_ID, type = CommandType.UUID, entityType = NicResponse.class, required = false, description = "The ID of the NIC to list IPs") private Long nicId; - @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, description = "the ID of the vm") + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, description = "The ID of the Instance") private Long vmId; - @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "list nic of the specific vm's network") + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "List NIC of the specific Instance's Network") private Long networkId; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; ///////////////////////////////////////////////////// @@ -155,7 +155,7 @@ public void execute() throws ResourceUnavailableException, ResourceAllocationExc this.setResponseObject(response); } } catch (Exception e) { - logger.warn("Failed to list secondary ip address per nic "); + logger.warn("Failed to list secondary ip address per NIC"); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMScheduleCmd.java index 3474f1ddcaad..c7a9a75f2eac 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMScheduleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMScheduleCmd.java @@ -20,36 +20,40 @@ 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.BaseListCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ResourceScheduleResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.VMScheduleResponse; -import org.apache.cloudstack.vm.schedule.VMSchedule; -import org.apache.cloudstack.vm.schedule.VMScheduleManager; +import org.apache.cloudstack.schedule.ResourceScheduleManager; import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; -@APICommand(name = "listVMSchedule", description = "List VM Schedules.", responseObject = VMScheduleResponse.class, +@Deprecated +@APICommand(name = "listVMSchedule", description = "List Instance Schedules.", responseObject = VMScheduleResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.19.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) public class ListVMScheduleCmd extends BaseListCmd { @Inject - VMScheduleManager vmScheduleManager; + ResourceScheduleManager resourceScheduleManager; @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, - description = "ID of the VM for which schedule is to be defined") + description = "ID of the Instance for which schedule is to be defined") private Long vmId; @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VMScheduleResponse.class, required = false, - description = "ID of VM schedule") + description = "ID of Instance schedule") private Long id; @Parameter(name = ApiConstants.ACTION, @@ -61,12 +65,12 @@ public class ListVMScheduleCmd extends BaseListCmd { @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, required = false, - description = "ID of VM schedule") + description = "Filter by enabled status") private Boolean enabled; - ///////////////////////////////////////////////////// - /////////////////// Accessors /////////////////////// - ///////////////////////////////////////////////////// + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// public Long getVmId() { return vmId; @@ -84,14 +88,26 @@ public Boolean getEnabled() { return enabled; } - ///////////////////////////////////////////////////// - /////////////// API Implementation/////////////////// - ///////////////////////////////////////////////////// + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// @Override public void execute() { - ListResponse response = vmScheduleManager.listSchedule(this); + String resourceIdStr = getVmId() != null ? String.valueOf(getVmId()) : null; + + ListResponse scheduleResponse = resourceScheduleManager.listSchedule( + getId(), null, ApiCommandResourceType.VirtualMachine, resourceIdStr, getAction(), getEnabled(), + getStartIndex(), getPageSizeVal() + ); + + List vmScheduleResponses = new ArrayList<>(); + for (ResourceScheduleResponse resourceScheduleResponse : scheduleResponse.getResponses()) { + vmScheduleResponses.add(new VMScheduleResponse(resourceScheduleResponse)); + } + ListResponse response = new ListResponse<>(); + response.setResponses(vmScheduleResponses, scheduleResponse.getCount()); response.setResponseName(getCommandName()); - response.setObjectName(VMSchedule.class.getSimpleName().toLowerCase()); + response.setObjectName("vmschedule"); setResponseObject(response); } } 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 a561b6fd24f5..46e3c7926f4e 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 @@ -64,7 +64,7 @@ import com.cloud.vm.VirtualMachine; -@APICommand(name = "listVirtualMachines", description = "List the virtual machines owned by the account.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, +@APICommand(name = "listVirtualMachines", description = "List the Instances owned by the Account.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements UserCmd { @@ -73,90 +73,90 @@ public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.GROUP_ID, type = CommandType.UUID, entityType = InstanceGroupResponse.class, description = "the group ID") + @Parameter(name = ApiConstants.GROUP_ID, type = CommandType.UUID, entityType = InstanceGroupResponse.class, description = "The group ID") private Long groupId; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, description = "the ID of the virtual machine") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, description = "The ID of the Instance") private Long id; - @Parameter(name=ApiConstants.IDS, type=CommandType.LIST, collectionType=CommandType.UUID, entityType=UserVmResponse.class, description="the IDs of the virtual machines, mutually exclusive with id", since = "4.4") + @Parameter(name=ApiConstants.IDS, type=CommandType.LIST, collectionType=CommandType.UUID, entityType=UserVmResponse.class, description = "The IDs of the Instances, mutually exclusive with id", since = "4.4") private List ids; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "name of the virtual machine (a substring match is made against the parameter value, data for all matching VMs will be returned)") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Name of the Instance (a substring match is made against the parameter value, data for all matching Instances will be returned)") private String name; - @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "state of the virtual machine. Possible values are: Running, Stopped, Present, Destroyed, Expunged. Present is used for the state equal not destroyed.") + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "State of the Instance. Possible values are: Running, Stopped, Present, Destroyed, Expunged. Present is used for the state equal not destroyed.") private String state; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the availability zone ID") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The availability zone ID") private Long zoneId; @Parameter(name = ApiConstants.FOR_VIRTUAL_NETWORK, type = CommandType.BOOLEAN, - description = "list by network type; true if need to list vms using Virtual Network, false otherwise") + description = "List by Network type; true if need to list Instances using Virtual Network, false otherwise") private Boolean forVirtualNetwork; - @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "list by network id") + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "List by Network ID") private Long networkId; - @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "the target hypervisor for the template") + @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "The target hypervisor for the Template") private String hypervisor; @Parameter(name = ApiConstants.DETAILS, type = CommandType.LIST, collectionType = CommandType.STRING, - description = "comma separated list of vm details requested, " + description = "Comma separated list of Instance details requested, " + "value can be a list of [all, group, nics, stats, secgrp, tmpl, servoff, diskoff, backoff, iso, volume, min, affgrp]." + " When no parameters are passed, all the details are returned if list.vm.default.details.stats is true (default)," + " otherwise when list.vm.default.details.stats is false the API response will exclude the stats details.") private List viewDetails; - @Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "list vms by template") + @Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "List Instances by Template") private Long templateId; - @Parameter(name = ApiConstants.ISO_ID, type = CommandType.UUID, entityType = IsoVmResponse.class, description = "list vms by iso") + @Parameter(name = ApiConstants.ISO_ID, type = CommandType.UUID, entityType = IsoVmResponse.class, description = "List Instances by ISO") private Long isoId; - @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, description = "list vms by vpc") + @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, description = "List Instances by VPC") private Long vpcId; - @Parameter(name = ApiConstants.AFFINITY_GROUP_ID, type = CommandType.UUID, entityType = AffinityGroupResponse.class, description = "list vms by affinity group") + @Parameter(name = ApiConstants.AFFINITY_GROUP_ID, type = CommandType.UUID, entityType = AffinityGroupResponse.class, description = "List Instances by affinity group") private Long affinityGroupId; - @Parameter(name = ApiConstants.SSH_KEYPAIR, type = CommandType.STRING, description = "list vms by ssh keypair name") + @Parameter(name = ApiConstants.SSH_KEYPAIR, type = CommandType.STRING, description = "List Instances by SSH keypair name") private String keypair; - @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, description = "list by the service offering", since = "4.4") + @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, description = "List by the service offering", since = "4.4") private Long serviceOffId; - @Parameter(name = ApiConstants.BACKUP_OFFERING_ID, type = CommandType.UUID, entityType = BackupOfferingResponse.class, description = "list by the backup offering", since = "4.17") + @Parameter(name = ApiConstants.BACKUP_OFFERING_ID, type = CommandType.UUID, entityType = BackupOfferingResponse.class, description = "List by the backup offering", since = "4.17") private Long backupOffId; - @Parameter(name = ApiConstants.DISPLAY_VM, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.DISPLAY_VM, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; - @Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class, required = false, description = "the user ID that created the VM and is under the account that owns the VM") + @Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class, required = false, description = "The user ID that created the Instance and is under the Account that owns the Instance") private Long userId; - @Parameter(name = ApiConstants.SECURITY_GROUP_ID, type = CommandType.UUID, entityType = SecurityGroupResponse.class, description = "the security group ID", since = "4.15") + @Parameter(name = ApiConstants.SECURITY_GROUP_ID, type = CommandType.UUID, entityType = SecurityGroupResponse.class, description = "The security group ID", since = "4.15") private Long securityGroupId; - @Parameter(name = ApiConstants.HA_ENABLE, type = CommandType.BOOLEAN, description = "list by the High Availability offering; true if filtering VMs with HA enabled; false for VMs with HA disabled", since = "4.15") + @Parameter(name = ApiConstants.HA_ENABLE, type = CommandType.BOOLEAN, description = "List by the High Availability offering; true if filtering Instances with HA enabled; false for Instances with HA disabled", since = "4.15") private Boolean haEnabled; - @Parameter(name = ApiConstants.AUTOSCALE_VMGROUP_ID, type = CommandType.UUID, entityType = AutoScaleVmGroupResponse.class, description = "the ID of AutoScaling VM Group", since = "4.18.0") + @Parameter(name = ApiConstants.AUTOSCALE_VMGROUP_ID, type = CommandType.UUID, entityType = AutoScaleVmGroupResponse.class, description = "The ID of AutoScaling Instance Group", since = "4.18.0") private Long autoScaleVmGroupId; @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, - description = "flag to display the resource icon for VMs", since = "4.16.0.0") + description = "Flag to display the resource icon for Instances", since = "4.16.0.0") private Boolean showIcon; @Parameter(name = ApiConstants.ACCUMULATE, type = CommandType.BOOLEAN, - description = "Accumulates the VM metrics data instead of returning only the most recent data collected. The default behavior is set by the global configuration vm.stats.increment.metrics.", + description = "Accumulates the Instance metrics data instead of returning only the most recent data collected. The default behavior is set by the global configuration vm.stats.increment.metrics.", since = "4.17.0") private Boolean accumulate; - @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") + @Parameter(name = ApiConstants.USER_DATA, type = CommandType.BOOLEAN, description = "Whether to return the Instances' 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") diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RebootVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RebootVMCmd.java index 153f5ea65636..6f4431547848 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RebootVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RebootVMCmd.java @@ -39,7 +39,7 @@ import com.cloud.uservm.UserVm; import com.cloud.vm.VirtualMachine; -@APICommand(name = "rebootVirtualMachine", description = "Reboots a virtual machine.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, +@APICommand(name = "rebootVirtualMachine", description = "Reboots an Instance.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class RebootVMCmd extends BaseAsyncCmd implements UserCmd { private static final String s_name = "rebootvirtualmachineresponse"; @@ -49,10 +49,10 @@ public class RebootVMCmd extends BaseAsyncCmd implements UserCmd { ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType=UserVmResponse.class, - required=true, description="The ID of the virtual machine") + required=true, description = "The ID of the Instance") private Long id; - @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force reboot the VM (VM is Stopped and then Started)", since = "4.16.0") + @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force reboot the Instance (It is Stopped and then Started)", since = "4.16.0") private Boolean forced; @Parameter(name = ApiConstants.BOOT_INTO_SETUP, type = CommandType.BOOLEAN, required = false, description = "Boot into hardware setup menu or not", since = "4.15.0.0") @@ -100,7 +100,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "rebooting user vm: " + this._uuidMgr.getUuid(VirtualMachine.class, getId()); + return "Rebooting User Instance with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -115,7 +115,7 @@ public Long getApiResourceId() { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException { - CallContext.current().setEventDetails("Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getId())); + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.ID)); UserVm result; result = _userVmService.rebootVirtualMachine(this); if (result !=null){ @@ -123,7 +123,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to reboot vm instance"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to reboot Instance"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RemoveIpFromVmNicCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RemoveIpFromVmNicCmd.java index 2f53c3d4e4cd..f4c4d82b30d4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RemoveIpFromVmNicCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RemoveIpFromVmNicCmd.java @@ -48,7 +48,7 @@ public class RemoveIpFromVmNicCmd extends BaseAsyncCmd { type = CommandType.UUID, required = true, entityType = NicSecondaryIpResponse.class, - description = "the ID of the secondary ip address to nic") + description = "The ID of the secondary ip address to NIC") private Long id; // unexposed parameter needed for events logging @@ -93,7 +93,7 @@ public NicSecondaryIp getIpEntry() { @Override public String getEventDescription() { - return ("Disassociating ip address with id=" + id); + return "Disassociating IP address with ID:" + getResourceUuid(ApiConstants.ID); } ///////////////////////////////////////////////////// @@ -132,7 +132,7 @@ private boolean isZoneSGEnabled() { @Override public void execute() throws InvalidParameterValueException { - CallContext.current().setEventDetails("Ip Id: " + id); + CallContext.current().setEventDetails("IP address ID: " + getResourceUuid(ApiConstants.ID)); NicSecondaryIp nicSecIp = getIpEntry(); if (nicSecIp == null) { @@ -159,10 +159,10 @@ public void execute() throws InvalidParameterValueException { SuccessResponse response = new SuccessResponse(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove secondary ip address for the nic"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove secondary IP address for the NIC"); } } catch (InvalidParameterValueException e) { - throw new InvalidParameterValueException("Removing guest ip from nic failed"); + throw new InvalidParameterValueException("Removing guest IP from NIC failed"); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RemoveNicFromVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RemoveNicFromVMCmd.java index d9024f340228..cfbc64339909 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RemoveNicFromVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RemoveNicFromVMCmd.java @@ -19,8 +19,6 @@ import java.util.ArrayList; import java.util.EnumSet; -import com.cloud.vm.Nic; - import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.APICommand; @@ -41,7 +39,7 @@ import com.cloud.uservm.UserVm; import com.cloud.vm.VirtualMachine; -@APICommand(name = "removeNicFromVirtualMachine", description = "Removes VM from specified network by deleting a NIC", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, +@APICommand(name = "removeNicFromVirtualMachine", description = "Removes Instance from specified Network by deleting a NIC", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class RemoveNicFromVMCmd extends BaseAsyncCmd implements UserCmd { private static final String s_name = "removenicfromvirtualmachineresponse"; @@ -51,7 +49,7 @@ public class RemoveNicFromVMCmd extends BaseAsyncCmd implements UserCmd { ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) @Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.UUID, entityType=UserVmResponse.class, - required=true, description="Virtual Machine ID") + required=true, description = "Instance ID") private Long vmId; @Parameter(name = ApiConstants.NIC_ID, type = CommandType.UUID, entityType = NicResponse.class, required = true, description = "NIC ID") @@ -89,7 +87,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Removing NIC " + this._uuidMgr.getUuid(Nic.class, getNicId()) + " from user vm: " + this._uuidMgr.getUuid(VirtualMachine.class, getVmId()); + return "Removing NIC with ID: " + getResourceUuid(ApiConstants.NIC_ID) + " from User Instance: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID); } @Override @@ -103,7 +101,7 @@ public long getEntityOwnerId() { @Override public void execute() { - CallContext.current().setEventDetails("Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getVmId()) + " Nic Id: " + this._uuidMgr.getUuid(Nic.class, getNicId())); + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID) + " NIC ID: " + getResourceUuid(ApiConstants.NIC_ID)); UserVm result = _userVmService.removeNicFromVirtualMachine(this); ArrayList dc = new ArrayList(); dc.add(VMDetails.valueOf("nics")); @@ -113,7 +111,7 @@ public void execute() { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove NIC from vm, see error log for details"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove NIC from Instance, see error log for details"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ResetVMPasswordCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ResetVMPasswordCmd.java index 7270004aeed8..b6179efc0d35 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ResetVMPasswordCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ResetVMPasswordCmd.java @@ -39,8 +39,8 @@ import com.cloud.uservm.UserVm; import com.cloud.vm.VirtualMachine; -@APICommand(name = "resetPasswordForVirtualMachine", responseObject=UserVmResponse.class, description="Resets the password for virtual machine. " + - "The virtual machine must be in a \"Stopped\" state and the template must already " + +@APICommand(name = "resetPasswordForVirtualMachine", responseObject=UserVmResponse.class, description = "Resets the password for the Instance. " + + "The Instance must be in a \"Stopped\" state and the Template must already " + "support this feature for this command to take effect. [async]", responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class ResetVMPasswordCmd extends BaseAsyncCmd implements UserCmd { @@ -52,7 +52,7 @@ public class ResetVMPasswordCmd extends BaseAsyncCmd implements UserCmd { ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType=UserVmResponse.class, - required=true, description="The ID of the virtual machine") + required=true, description = "The ID of the Instance") private Long id; @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, description="The new password of the virtual machine. If null, a random password will be generated for the VM.", since="4.19.0") @@ -101,7 +101,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "resetting password for vm: " + getId(); + return "Resetting password for Instance with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -120,18 +120,18 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE UserVm vm = _responseGenerator.findUserVmById(getId()); if (StringUtils.isBlank(password)) { password = _mgr.generateRandomPassword(); - logger.debug(String.format("Resetting VM [%s] password to a randomly generated password.", vm.getUuid())); + logger.debug("Resetting VM [{}] password to a randomly generated password.", vm.getUuid()); } else { - logger.debug(String.format("Resetting VM [%s] password to password defined by user.", vm.getUuid())); + logger.debug("Resetting VM [{}] password to password defined by user.", vm.getUuid()); } - CallContext.current().setEventDetails("Vm Id: " + getId()); + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.ID)); UserVm result = _userVmService.resetVMPassword(this, password); if (result != null){ UserVmResponse response = _responseGenerator.createUserVmResponse(getResponseView(), "virtualmachine", result).get(0); response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to reset vm password"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to reset Instance password"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ResetVMSSHKeyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ResetVMSSHKeyCmd.java index a4019411e1d2..73e4ec623f23 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ResetVMSSHKeyCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ResetVMSSHKeyCmd.java @@ -44,8 +44,8 @@ import java.util.ArrayList; import java.util.List; -@APICommand(name = "resetSSHKeyForVirtualMachine", responseObject = UserVmResponse.class, description = "Resets the SSH Key for virtual machine. " + - "The virtual machine must be in a \"Stopped\" state. [async]", responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, +@APICommand(name = "resetSSHKeyForVirtualMachine", responseObject = UserVmResponse.class, description = "Resets the SSH Key for the Instance. " + + "The Instance must be in a \"Stopped\" state. [async]", responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class ResetVMSSHKeyCmd extends BaseAsyncCmd implements UserCmd { @@ -56,27 +56,27 @@ public class ResetVMSSHKeyCmd extends BaseAsyncCmd implements UserCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, description = "The ID of the virtual machine") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, description = "The ID of the Instance") private Long id; @Deprecated - @Parameter(name = ApiConstants.SSH_KEYPAIR, type = CommandType.STRING ,description = "name of the ssh key pair used to login to the virtual machine") + @Parameter(name = ApiConstants.SSH_KEYPAIR, type = CommandType.STRING ,description = "Name of the SSH key pair used to login to the Instance") String name; - @Parameter(name = ApiConstants.SSH_KEYPAIRS, type = CommandType.LIST, collectionType = CommandType.STRING, since="4.17", description = "names of the ssh key pairs to be used to login to the virtual machine") + @Parameter(name = ApiConstants.SSH_KEYPAIRS, type = CommandType.LIST, collectionType = CommandType.STRING, since="4.17", description = "Names of the SSH key pairs to be used to login to the Instance") List names; //Owner information - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the ssh key. Must be used with domainId.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "An optional Account for the SSH key. 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.") + description = "An optional domainId for the Instance. 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 ssh key") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "An optional project for the SSH key") private Long projectId; ///////////////////////////////////////////////////// @@ -121,7 +121,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "resetting SSHKey for vm: " + getId(); + return "Resetting SSH key for Instance with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -152,7 +152,7 @@ public Long getApiResourceId() { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException { - CallContext.current().setEventDetails("Vm Id: " + getId()); + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.ID)); UserVm result = _userVmService.resetVMSSHKey(this); if (result != null) { @@ -160,7 +160,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to reset vm SSHKey"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to reset SSHKey"); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ResetVMUserDataCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ResetVMUserDataCmd.java index 0ecf4ff13845..8c513549506f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ResetVMUserDataCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ResetVMUserDataCmd.java @@ -40,8 +40,8 @@ import java.util.Map; -@APICommand(name = "resetUserDataForVirtualMachine", responseObject = UserVmResponse.class, description = "Resets the UserData for virtual machine. " + - "The virtual machine must be in a \"Stopped\" state.", responseView = ResponseObject.ResponseView.Restricted, entityType = {VirtualMachine.class}, +@APICommand(name = "resetUserDataForVirtualMachine", responseObject = UserVmResponse.class, description = "Resets the UserData for Instance. " + + "The Instance must be in a \"Stopped\" state.", responseView = ResponseObject.ResponseView.Restricted, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true, since = "4.18.0") public class ResetVMUserDataCmd extends BaseCmd implements UserCmd { @@ -52,12 +52,12 @@ public class ResetVMUserDataCmd extends BaseCmd implements UserCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @ACL(accessType = SecurityChecker.AccessType.OperateEntry) - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, description = "The ID of the virtual machine") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, description = "The ID of the Instance") private Long id; @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. " + + description = "An optional binary data that can be sent to the Instance 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. " + @@ -65,23 +65,23 @@ public class ResetVMUserDataCmd extends BaseCmd implements UserCmd { length = 1048576) private String userData; - @Parameter(name = ApiConstants.USER_DATA_ID, type = CommandType.UUID, entityType = UserDataResponse.class, description = "the ID of the userdata") + @Parameter(name = ApiConstants.USER_DATA_ID, type = CommandType.UUID, entityType = UserDataResponse.class, description = "The ID of the userdata") private Long userdataId; - @Parameter(name = ApiConstants.USER_DATA_DETAILS, type = CommandType.MAP, description = "used to specify the parameters values for the variables in userdata.") + @Parameter(name = ApiConstants.USER_DATA_DETAILS, type = CommandType.MAP, description = "Used to specify the parameters values for the variables in userdata.") private Map userdataDetails; //Owner information - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the virtual machine. Must be used with domainId.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "An optional Account for the Instance. 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.") + description = "An optional domainId for the Instance. 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 virtual machine") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "An optional project for the Instance") private Long projectId; ///////////////////////////////////////////////////// @@ -143,7 +143,7 @@ public Long getApiResourceId() { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException { - CallContext.current().setEventDetails("Vm Id: " + getId()); + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.ID)); UserVm result = _userVmService.resetVMUserData(this); if (result != null) { @@ -151,7 +151,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to reset vm SSHKey"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to reset SSHKey"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RestoreVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RestoreVMCmd.java index 3839049eee5e..d3459347687a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RestoreVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RestoreVMCmd.java @@ -44,7 +44,7 @@ import java.util.Map; -@APICommand(name = "restoreVirtualMachine", description = "Restore a VM to original template/ISO or new template/ISO", responseObject = UserVmResponse.class, since = "3.0.0", responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, +@APICommand(name = "restoreVirtualMachine", description = "Restore an Instance to original Template/ISO or new Template/ISO", responseObject = UserVmResponse.class, since = "3.0.0", responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class RestoreVMCmd extends BaseAsyncCmd implements UserCmd { @@ -52,13 +52,13 @@ public class RestoreVMCmd extends BaseAsyncCmd implements UserCmd { @ACL(accessType = AccessType.OperateEntry) @Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.UUID, entityType=UserVmResponse.class, - required=true, description="Virtual Machine ID") + required=true, description = "Instance ID") private Long vmId; @Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, - description = "an optional template Id to restore vm from the new template. This can be an ISO id in case of restore vm deployed using ISO") + description = "An optional Template Id to restore Instance from the new Template. This can be an ISO id in case of restore Instance deployed using ISO") private Long templateId; @Parameter(name = ApiConstants.DISK_OFFERING_ID, @@ -90,21 +90,21 @@ public String getEventType() { @Override public String getEventDescription() { - return "Restore a VM to original template or specific snapshot"; + return "Restoring Instance with ID: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID) + " to original Template or specific Snapshot"; } @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { UserVm result; - CallContext.current().setEventDetails("Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getVmId())); + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID)); result = _userVmService.restoreVM(this); if (result != null) { UserVmResponse response = _responseGenerator.createUserVmResponse(getResponseView(), "virtualmachine", result).get(0); response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to restore vm " + getVmId()); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to restore Instance " + getVmId()); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ScaleVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ScaleVMCmd.java index 3af6d5245f00..36d0ad9c6500 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ScaleVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ScaleVMCmd.java @@ -39,13 +39,12 @@ import com.cloud.exception.ManagementServerException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.VirtualMachineMigrationException; -import com.cloud.offering.ServiceOffering; import com.cloud.user.Account; import com.cloud.uservm.UserVm; import com.cloud.vm.VirtualMachine; -@APICommand(name = "scaleVirtualMachine", description = "Scales the virtual machine to a new service offering. This command also considers the volume size in the service offering or disk offering linked to the new service offering and apply all characteristics to the root volume.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, +@APICommand(name = "scaleVirtualMachine", description = "Scales the Instance to a new service offering. This command also considers the volume size in the service offering or disk offering linked to the new service offering and apply all characteristics to the root volume.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ScaleVMCmd extends BaseAsyncCmd implements UserCmd { private static final String s_name = "scalevirtualmachineresponse"; @@ -55,14 +54,14 @@ public class ScaleVMCmd extends BaseAsyncCmd implements UserCmd { ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType=UserVmResponse.class, - required=true, description="The ID of the virtual machine") + required=true, description = "The ID of the Instance") private Long id; @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") + required=true, description = "The ID of the service offering for the Instance") private Long serviceOfferingId; - @Parameter(name = ApiConstants.DETAILS, type = BaseCmd.CommandType.MAP, description = "name value pairs of custom parameters for cpuspeed, memory and cpunumber. example details[i].name=value") + @Parameter(name = ApiConstants.DETAILS, type = BaseCmd.CommandType.MAP, description = "Name value pairs of custom parameters for cpuspeed, memory and cpunumber. example details[i].name=value") private Map details; @Parameter(name = ApiConstants.MIN_IOPS, type = CommandType.LONG, required = false, description = "New minimum number of IOPS for the custom disk offering", since = "4.17") @@ -148,7 +147,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "upgrading vm: " + this._uuidMgr.getUuid(VirtualMachine.class, getId()) + " to service offering: " + this._uuidMgr.getUuid(ServiceOffering.class, getServiceOfferingId()); + return "Upgrading Instance with ID: " + getResourceUuid(ApiConstants.ID) + " to service offering with ID: " + getResourceUuid(ApiConstants.SERVICE_OFFERING_ID); } @Override @@ -185,7 +184,7 @@ public void execute() { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to scale vm"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to scale Instance"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/StartVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/StartVMCmd.java index c0311d599974..40ae91d4c264 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/StartVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/StartVMCmd.java @@ -47,7 +47,7 @@ import com.cloud.utils.exception.ExecutionException; import com.cloud.vm.VirtualMachine; -@APICommand(name = "startVirtualMachine", responseObject = UserVmResponse.class, description = "Starts a virtual machine.", responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, +@APICommand(name = "startVirtualMachine", responseObject = UserVmResponse.class, description = "Starts an Instance.", responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class StartVMCmd extends BaseAsyncCmd implements UserCmd { @@ -58,37 +58,37 @@ public class StartVMCmd extends BaseAsyncCmd implements UserCmd { // /////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType=UserVmResponse.class, - required = true, description = "The ID of the virtual machine") + required = true, description = "The ID of the Instance") private Long id; @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") + description = "Destination Pod ID to deploy the Instance to - parameter available for root admin only") 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") + description = "Destination Cluster ID to deploy the Instance to - parameter available for root admin only") private Long clusterId; @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", + description = "Destination Host ID to deploy the Instance to - parameter available for root admin only", since = "3.0.1") private Long hostId; @Parameter(name = ApiConstants.CONSIDER_LAST_HOST, type = CommandType.BOOLEAN, - description = "True by default, CloudStack will firstly try to start the VM on the last host where it run on before stopping, if destination host is not specified. " + - "If false, CloudStack will not consider the last host and start the VM by normal process.", + description = "True by default, CloudStack will firstly try to start the Instance on the last host where it run on before stopping, if destination host is not specified. " + + "If false, CloudStack will not consider the last host and start the Instance by normal process.", since = "4.18.0", authorized = {RoleType.Admin}) private Boolean considerLastHost; - @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 }) + @Parameter(name = ApiConstants.DEPLOYMENT_PLANNER, type = CommandType.STRING, description = "Deployment planner to use for Instance allocation. Available to ROOT admin only", since = "4.4", authorized = { RoleType.Admin }) private String deploymentPlanner; @Parameter(name = ApiConstants.BOOT_INTO_SETUP, type = CommandType.BOOLEAN, required = false, description = "Boot into hardware setup menu or not", since = "4.15.0.0") @@ -161,7 +161,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "starting user vm: " + this._uuidMgr.getUuid(VirtualMachine.class, getId()); + return "Starting User Instance with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -177,7 +177,7 @@ public Long getApiResourceId() { @Override public void execute() { try { - CallContext.current().setEventDetails("Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getId())); + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.ID)); UserVm result; result = _userVmService.startVirtualMachine(this); @@ -187,7 +187,7 @@ public void execute() { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to start a vm"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to start an Instance"); } } catch (ConcurrentOperationException ex) { logger.warn("Exception: ", ex); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/StopVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/StopVMCmd.java index bfd5d8d07f61..232eeebd34be 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/StopVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/StopVMCmd.java @@ -37,7 +37,7 @@ import com.cloud.uservm.UserVm; import com.cloud.vm.VirtualMachine; -@APICommand(name = "stopVirtualMachine", responseObject = UserVmResponse.class, description = "Stops a virtual machine.", responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, +@APICommand(name = "stopVirtualMachine", responseObject = UserVmResponse.class, description = "Stops an Instance.", responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class StopVMCmd extends BaseAsyncCmd implements UserCmd { @@ -49,12 +49,12 @@ public class StopVMCmd extends BaseAsyncCmd implements UserCmd { @ACL(accessType = AccessType.OperateEntry) @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType=UserVmResponse.class, - required = true, description = "The ID of the virtual machine") + required = true, description = "The ID of the Instance") private Long id; - @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force stop the VM " - + "(vm is marked as Stopped even when command fails to be send to the backend, otherwise a force poweroff is attempted)." - + " This option is to be used if the caller knows the VM is stopped and should be marked as such.") + @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force stop the Instance " + + "(Instance is marked as Stopped even when command fails to be send to the backend, otherwise a force poweroff is attempted)." + + " This option is to be used if the caller knows the Instance is stopped and should be marked as such.") private Boolean forced; // /////////////////////////////////////////////////// @@ -96,7 +96,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "stopping user vm: " + this._uuidMgr.getUuid(VirtualMachine.class, getId()); + return "Stopping User Instance with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -115,7 +115,7 @@ public boolean isForced() { @Override public void execute() throws ServerApiException, ConcurrentOperationException { - CallContext.current().setEventDetails("Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getId())); + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.ID)); UserVm result; result = _userVmService.stopVirtualMachine(getId(), isForced()); @@ -125,7 +125,7 @@ public void execute() throws ServerApiException, ConcurrentOperationException { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to stop vm"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to stop Instance"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateDefaultNicForVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateDefaultNicForVMCmd.java index 837bde06a6ca..011edb1a9df4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateDefaultNicForVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateDefaultNicForVMCmd.java @@ -19,8 +19,6 @@ import java.util.ArrayList; import java.util.EnumSet; -import com.cloud.vm.Nic; - import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.APICommand; @@ -41,7 +39,7 @@ import com.cloud.uservm.UserVm; import com.cloud.vm.VirtualMachine; -@APICommand(name = "updateDefaultNicForVirtualMachine", description = "Changes the default NIC on a VM", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, +@APICommand(name = "updateDefaultNicForVirtualMachine", description = "Changes the default NIC on an Instance", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class UpdateDefaultNicForVMCmd extends BaseAsyncCmd implements UserCmd { private static final String s_name = "updatedefaultnicforvirtualmachineresponse"; @@ -52,7 +50,7 @@ public class UpdateDefaultNicForVMCmd extends BaseAsyncCmd implements UserCmd { @ACL(accessType = AccessType.OperateEntry) @Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.UUID, entityType=UserVmResponse.class, - required=true, description="Virtual Machine ID") + required=true, description = "Instance ID") private Long vmId; @Parameter(name = ApiConstants.NIC_ID, type = CommandType.UUID, entityType = NicResponse.class, required = true, description = "NIC ID") @@ -90,7 +88,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Updating NIC " + this._uuidMgr.getUuid(Nic.class, getNicId()) + " on user vm: " + this._uuidMgr.getUuid(VirtualMachine.class, getVmId()); + return "Setting NIC " + getResourceUuid(ApiConstants.NIC_ID) + " as default to User Instance: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID); } @Override @@ -104,7 +102,7 @@ public long getEntityOwnerId() { @Override public void execute() { - CallContext.current().setEventDetails("Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getVmId()) + " Nic Id: " + this._uuidMgr.getUuid(Nic.class, getNicId())); + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID) + " NIC ID: " + getResourceUuid(ApiConstants.NIC_ID)); UserVm result = _userVmService.updateDefaultNicForVirtualMachine(this); ArrayList dc = new ArrayList(); dc.add(VMDetails.valueOf("nics")); @@ -114,7 +112,7 @@ public void execute() { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to set default nic for VM. Refer to server logs for details."); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to set default NIC for Instance. Refer to server logs for details."); } } } 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 7906ec632a49..a5f06e6ecbb3 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 @@ -21,6 +21,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.user.Account; import com.cloud.uservm.UserVm; +import com.cloud.utils.StringUtils; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.Dhcp; import com.cloud.vm.VirtualMachine; @@ -44,16 +45,15 @@ 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 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. " + - "Therefore, stop the VM manually before issuing this call.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, +@APICommand(name = "updateVirtualMachine", description = "Updates properties of an Instance. The Instance has to be stopped and restarted for the " + + "new properties to take effect. UpdateVirtualMachine does not first check whether the Instance is stopped. " + + "Therefore, stop the Instance manually before issuing this call.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class UpdateVMCmd extends BaseCustomIdCmd implements SecurityGroupAction, UserCmd { private static final String s_name = "updatevirtualmachineresponse"; @@ -62,29 +62,29 @@ public class UpdateVMCmd extends BaseCustomIdCmd implements SecurityGroupAction, //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.DISPLAY_NAME, type = CommandType.STRING, description = "user generated name") + @Parameter(name = ApiConstants.DISPLAY_NAME, type = CommandType.STRING, description = "User generated name") private String displayName; - @Parameter(name = ApiConstants.GROUP, type = CommandType.STRING, description = "group of the virtual machine") + @Parameter(name = ApiConstants.GROUP, type = CommandType.STRING, description = "Group of the Instance") private String group; - @Parameter(name = ApiConstants.HA_ENABLE, type = CommandType.BOOLEAN, description = "true if high-availability is enabled for the virtual machine, false otherwise") + @Parameter(name = ApiConstants.HA_ENABLE, type = CommandType.BOOLEAN, description = "True if high-availability is enabled for the Instance, false otherwise") private Boolean haEnable; @ACL(accessType = AccessType.OperateEntry) @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType=UserVmResponse.class, - required=true, description="The ID of the virtual machine") + required=true, description = "The ID of the Instance") private Long id; @Parameter(name = ApiConstants.OS_TYPE_ID, type = CommandType.UUID, entityType = GuestOSResponse.class, - description = "the ID of the OS type that best represents this VM.") + description = "The ID of the OS type that best represents this Instance.") private Long osTypeId; @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. " + + description = "An optional binary data that can be sent to the Instance 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. " + @@ -93,24 +93,24 @@ public class UpdateVMCmd extends BaseCustomIdCmd implements SecurityGroupAction, since = "4.16.0") private String userData; - @Parameter(name = ApiConstants.USER_DATA_ID, type = CommandType.UUID, entityType = UserDataResponse.class, description = "the ID of the userdata", since = "4.18") + @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") + @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; - @Parameter(name = ApiConstants.DISPLAY_VM, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the vm to the end user or not.", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.DISPLAY_VM, type = CommandType.BOOLEAN, description = "An optional field, whether to the display the Instance to the end User or not.", authorized = {RoleType.Admin}) private Boolean displayVm; @Parameter(name = ApiConstants.IS_DYNAMICALLY_SCALABLE, type = CommandType.BOOLEAN, - description = "true if VM contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory. This can be updated only when dynamic scaling is enabled on template, service offering and the corresponding global setting") + description = "True if Instance contains XS/VMWare tools in order to support dynamic scaling of Instance cpu/memory. This can be updated only when dynamic scaling is enabled on Template, service offering and the corresponding global setting") protected Boolean isDynamicallyScalable; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "new host name of the vm. The VM has to be stopped/started for this update to take affect", validations = {ApiArgValidator.RFCComplianceDomainName}, since = "4.4") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "New host name of the Instance. The Instance has to be stopped/started for this update to take affect", validations = {ApiArgValidator.RFCComplianceDomainName}, since = "4.4") private String name; - @Parameter(name = ApiConstants.INSTANCE_NAME, type = CommandType.STRING, description = "instance name of the user vm", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.INSTANCE_NAME, type = CommandType.STRING, description = "Instance name of the User Instance", since = "4.4", authorized = {RoleType.Admin}) private String instanceName; @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "Details in key/value pairs. 'extraconfig' is not allowed to be passed in details.") @@ -121,7 +121,7 @@ public class UpdateVMCmd extends BaseCustomIdCmd implements SecurityGroupAction, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = SecurityGroupResponse.class, - description = "list of security group ids to be applied on the virtual machine.") + description = "List of security group ids to be applied on the Instance.") private List securityGroupIdList; @ACL @@ -129,22 +129,22 @@ public class UpdateVMCmd extends BaseCustomIdCmd implements SecurityGroupAction, 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. " + + description = "Comma separated list of security groups names that going to be applied to the Instance. " + + "Should be passed only when Instance is created from a zone with Basic Network support. " + "Mutually exclusive with securitygroupids parameter" ) private List securityGroupNameList; @Parameter(name = ApiConstants.CLEAN_UP_DETAILS, type = CommandType.BOOLEAN, - description = "optional boolean field, which indicates if details should be cleaned up or not (if set to true, details removed for this resource, details field ignored; if false or not set, no action)") + description = "Optional boolean field, which indicates if details should be cleaned up or not (if set to true, details removed for this resource, details field ignored; if false or not set, no action)") private Boolean cleanupDetails; - @Parameter(name = ApiConstants.DHCP_OPTIONS_NETWORK_LIST, type = CommandType.MAP, description = "DHCP options which are passed to the VM on start up" + @Parameter(name = ApiConstants.DHCP_OPTIONS_NETWORK_LIST, type = CommandType.MAP, description = "DHCP options which are passed to the Instance 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.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) + @Parameter(name = ApiConstants.EXTRA_CONFIG, type = CommandType.STRING, since = "4.12", description = "An optional URL encoded string that can be passed to the Instance upon successful deployment", length = 5120) private String extraConfig; @Parameter(name = ApiConstants.DELETE_PROTECTION, @@ -163,11 +163,20 @@ public class UpdateVMCmd extends BaseCustomIdCmd implements SecurityGroupAction, description = "Lease expiry action, valid values are STOP and DESTROY") private String leaseExpiryAction; + @Parameter(name = ApiConstants.CLEAN_UP_EXTRA_CONFIG, type = CommandType.BOOLEAN, since = "4.23.0", + description = "Optional boolean field, which indicates if extraconfig for the instance should be " + + "cleaned up or not (If set to true, extraconfig removed for this instance, extraconfig field " + + "ignored; if false or not set, no action)") + private Boolean cleanupExtraConfig; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// public String getDisplayName() { + if (StringUtils.isBlank(displayName)) { + displayName = name; + } return displayName; } @@ -245,7 +254,7 @@ public Map> getDhcpOptionsMap() { String networkId = dhcpNetworkOptions.get(ApiConstants.NETWORK_ID); if(networkId == null) { - throw new IllegalArgumentException("No networkid specified when providing extra dhcp options."); + throw new IllegalArgumentException("No networkid specified when providing extra DHCP options."); } Map dhcpOptionsForNetwork = new HashMap<>(); @@ -271,14 +280,26 @@ public String getExtraConfig() { return extraConfig; } - ///////////////////////////////////////////////////// - /////////////// API Implementation/////////////////// - ///////////////////////////////////////////////////// - public Long getOsTypeId() { return osTypeId; } + public boolean isCleanupExtraConfig() { + return Boolean.TRUE.equals(cleanupExtraConfig); + } + + public void setId(Long id) { + this.id = id; + } + + public void setSecurityGroupIdList(List securityGroupIdList) { + this.securityGroupIdList = securityGroupIdList; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + @Override public String getCommandName() { return s_name; @@ -300,19 +321,19 @@ public long getEntityOwnerId() { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException { - CallContext.current().setEventDetails("Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getId())); + CallContext.current().setEventDetails("Instance ID: " + ApiConstants.ID); UserVm result = null; try { result = _userVmService.updateVirtualMachine(this); } catch (CloudRuntimeException e) { - throw new CloudRuntimeException(String.format("Failed to update VM, due to: %s", e.getLocalizedMessage()), e); + throw new CloudRuntimeException(String.format("Failed to update Instance, due to: %s", e.getLocalizedMessage()), e); } if (result != null) { UserVmResponse response = _responseGenerator.createUserVmResponse(getResponseView(), "virtualmachine", result).get(0); response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update vm"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update Instance"); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMScheduleCmd.java index 72f85c1a99ce..9b5b05373102 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMScheduleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMScheduleCmd.java @@ -22,28 +22,31 @@ import com.cloud.vm.VirtualMachine; 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.BaseCmd; import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.ResourceScheduleResponse; import org.apache.cloudstack.api.response.VMScheduleResponse; -import org.apache.cloudstack.vm.schedule.VMSchedule; -import org.apache.cloudstack.vm.schedule.VMScheduleManager; +import org.apache.cloudstack.schedule.ResourceSchedule; +import org.apache.cloudstack.schedule.ResourceScheduleManager; import javax.inject.Inject; import java.util.Date; -@APICommand(name = "updateVMSchedule", description = "Update VM Schedule.", responseObject = VMScheduleResponse.class, +@Deprecated +@APICommand(name = "updateVMSchedule", description = "Update Instance Schedule.", responseObject = VMScheduleResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.19.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) public class UpdateVMScheduleCmd extends BaseCmd { @Inject - VMScheduleManager vmScheduleManager; + ResourceScheduleManager resourceScheduleManager; @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VMScheduleResponse.class, required = true, - description = "ID of VM schedule") + description = "ID of Instance schedule") private Long id; @Parameter(name = ApiConstants.DESCRIPTION, @@ -55,7 +58,7 @@ public class UpdateVMScheduleCmd extends BaseCmd { @Parameter(name = ApiConstants.SCHEDULE, type = CommandType.STRING, required = false, - description = "Schedule for action on VM in cron format. e.g. '0 15 10 * *' for 'at 15:00 on 10th day of every month'") + description = "Schedule for action on Instance in cron format. e.g. '0 15 10 * *' for 'at 15:00 on 10th day of every month'") private String schedule; @Parameter(name = ApiConstants.TIMEZONE, @@ -67,26 +70,26 @@ public class UpdateVMScheduleCmd extends BaseCmd { @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, required = false, - description = "start date from which the schedule becomes active" - + "Use format \"yyyy-MM-dd hh:mm:ss\")") + description = "Start date from which the schedule becomes active" + + "(Format \"yyyy-MM-dd hh:mm:ss\")") private Date startDate; @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, required = false, - description = "end date after which the schedule becomes inactive" - + "Use format \"yyyy-MM-dd hh:mm:ss\")") + description = "End date after which the schedule becomes inactive" + + "(Format \"yyyy-MM-dd hh:mm:ss\")") private Date endDate; @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, required = false, - description = "Enable VM schedule") + description = "Enable Instance schedule") private Boolean enabled; - ///////////////////////////////////////////////////// - /////////////////// Accessors /////////////////////// - ///////////////////////////////////////////////////// + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// public Long getId() { return id; @@ -116,24 +119,29 @@ public Boolean getEnabled() { return enabled; } - ///////////////////////////////////////////////////// - /////////////// API Implementation/////////////////// - ///////////////////////////////////////////////////// + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// @Override public void execute() { - VMScheduleResponse response = vmScheduleManager.updateSchedule(this); + ResourceScheduleResponse scheduleResponse = resourceScheduleManager.updateSchedule( + getId(), getDescription(), getSchedule(), getTimeZone(), getStartDate(), getEndDate(), getEnabled(), null); + VMScheduleResponse response = new VMScheduleResponse(scheduleResponse); response.setResponseName(getCommandName()); setResponseObject(response); } @Override public long getEntityOwnerId() { - VMSchedule vmSchedule = _entityMgr.findById(VMSchedule.class, getId()); - if (vmSchedule == null) { - throw new InvalidParameterValueException(String.format("Unable to find vmSchedule by id=%d", getId())); + ResourceSchedule schedule = _entityMgr.findById(ResourceSchedule.class, getId()); + if (schedule == null || !ApiCommandResourceType.VirtualMachine.equals(schedule.getResourceType())) { + throw new InvalidParameterValueException(String.format("Unable to find VM schedule by id=%d", getId())); + } + VirtualMachine vm = _entityMgr.findById(VirtualMachine.class, schedule.getResourceId()); + if (vm == null) { + throw new InvalidParameterValueException(String.format("Unable to find VM schedule by id=%d", getId())); } - VirtualMachine vm = _entityMgr.findById(VirtualMachine.class, vmSchedule.getVmId()); return vm.getAccountId(); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVmNicCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVmNicCmd.java new file mode 100644 index 000000000000..363273a4670d --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVmNicCmd.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.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.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.NicResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; +import com.cloud.uservm.UserVm; + +import java.util.ArrayList; +import java.util.EnumSet; + +@APICommand(name = "updateVmNic", description = "Updates the specified VM NIC", responseObject = NicResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + authorized = { RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User }) +public class UpdateVmNicCmd extends BaseAsyncCmd { + + @ACL + @Parameter(name = ApiConstants.NIC_ID, type = CommandType.UUID, entityType = NicResponse.class, required = true, description = "NIC ID") + private Long nicId; + + @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, description = "If true, sets the NIC state to UP; otherwise, sets the NIC state to DOWN") + private Boolean enabled; + + public Long getNicId() { + return nicId; + } + + public Boolean isEnabled() { + return enabled; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_NIC_UPDATE; + } + + @Override + public String getEventDescription() { + return String.format("Updating NIC %s.", getResourceUuid(ApiConstants.NIC_ID)); + } + + @Override + public long getEntityOwnerId() { + UserVm vm = _responseGenerator.findUserVmByNicId(nicId); + if (vm == null) { + return Account.ACCOUNT_ID_SYSTEM; + } + return vm.getAccountId(); + } + + @Override + public void execute() { + CallContext.current().setEventDetails(String.format("NIC ID: %s", getResourceUuid(ApiConstants.NIC_ID))); + + UserVm result = _userVmService.updateVirtualMachineNic(this); + + ArrayList dc = new ArrayList<>(); + dc.add(ApiConstants.VMDetails.valueOf("nics")); + EnumSet details = EnumSet.copyOf(dc); + + if (result != null){ + UserVmResponse response = _responseGenerator.createUserVmResponse(ResponseObject.ResponseView.Restricted, "virtualmachine", details, result).get(0); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update NIC from VM."); + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVmNicIpCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVmNicIpCmd.java index 5c654701de91..6da34c7ef0b0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVmNicIpCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVmNicIpCmd.java @@ -46,14 +46,14 @@ import com.cloud.utils.net.NetUtils; import com.cloud.vm.Nic; -@APICommand(name = "updateVmNicIp", description = "Update the default Ip of a VM Nic", responseObject = UserVmResponse.class) +@APICommand(name = "updateVmNicIp", description = "Update the default IP of an Instance NIC", responseObject = UserVmResponse.class) public class UpdateVmNicIpCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @Parameter(name=ApiConstants.NIC_ID, type=CommandType.UUID, entityType = NicResponse.class, required = true, - description="the ID of the nic to which you want to assign private IP") + description = "The ID of the NIC to which you want to assign private IP") private Long nicId; @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, required = false, @@ -79,7 +79,7 @@ public long getDomainId() { private long getZoneId() { Network ntwk = _entityMgr.findById(Network.class, getNetworkId()); if (ntwk == null) { - throw new InvalidParameterValueException("Can't find zone id for specified"); + throw new InvalidParameterValueException("Can't find zone ID for specified"); } return ntwk.getDataCenterId(); } @@ -87,7 +87,7 @@ private long getZoneId() { public Long getNetworkId() { Nic nic = _entityMgr.findById(Nic.class, nicId); if (nic == null) { - throw new InvalidParameterValueException("Can't find network id for specified nic"); + throw new InvalidParameterValueException("Can't find Network ID for specified NIC"); } Long networkId = nic.getNetworkId(); return networkId; @@ -123,7 +123,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "associating ip to nic id: " + this._uuidMgr.getUuid(Network.class, getNetworkId()) + " in zone " + this._uuidMgr.getUuid(DataCenter.class, getZoneId()); + return "Associating IP to NIC with ID: " + getResourceUuid(ApiConstants.NIC_ID) + " in zone " + this._uuidMgr.getUuid(DataCenter.class, getZoneId()); } ///////////////////////////////////////////////////// @@ -139,11 +139,11 @@ public static String getResultObjectName() { public void execute() throws ResourceUnavailableException, ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException { - CallContext.current().setEventDetails("Nic Id: " + getNicId() ); + CallContext.current().setEventDetails("NIC ID: " + getResourceUuid(ApiConstants.NIC_ID)); String ip; if ((ip = getIpaddress()) != null) { if (!NetUtils.isValidIp4(ip)) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Invalid ip address " + ip); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Invalid IP address " + ip); } } @@ -156,7 +156,7 @@ public void execute() throws ResourceUnavailableException, ResourceAllocationExc response.setResponseName(getCommandName()); this.setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update ip address on vm NIC. Refer to server logs for details."); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update IP address on Instance NIC. Refer to server logs for details."); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java index 6a7422e70bbb..83908802a690 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java @@ -41,8 +41,8 @@ import com.cloud.vm.VirtualMachine; @Deprecated(since = "4.18") -@APICommand(name = "changeServiceForVirtualMachine", responseObject=UserVmResponse.class, description="(This API is deprecated, use scaleVirtualMachine API)" + - "Changes the service offering for a virtual machine. The virtual machine must be in a \"Stopped\" state for " + +@APICommand(name = "changeServiceForVirtualMachine", responseObject=UserVmResponse.class, description = "(This API is deprecated, use scaleVirtualMachine API)" + + "Changes the service offering for an Instance. The Instance must be in a \"Stopped\" state for " + "this command to take effect.", responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class UpgradeVMCmd extends BaseCmd implements UserCmd { @@ -54,14 +54,14 @@ public class UpgradeVMCmd extends BaseCmd implements UserCmd { @ACL(accessType = AccessType.OperateEntry) @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType=UserVmResponse.class, - required=true, description="The ID of the virtual machine") + required=true, description = "The ID of the Instance") private Long id; @Parameter(name=ApiConstants.SERVICE_OFFERING_ID, type=CommandType.UUID, entityType=ServiceOfferingResponse.class, - required=true, description="the service offering ID to apply to the virtual machine") + required=true, description = "The service offering ID to apply to the Instance") protected Long serviceOfferingId; - @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "name value pairs of custom parameters for cpuspeed, memory and cpunumber. example details[i].name=value") + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "Name value pairs of custom parameters for cpuspeed, memory and cpunumber. example details[i].name=value") private Map details; @Parameter(name = ApiConstants.MIN_IOPS, type = CommandType.LONG, required = false, description = "New minimum number of IOPS for the custom disk offering", since = "4.17") @@ -139,7 +139,7 @@ public long getEntityOwnerId() { @Override public void execute() throws ResourceAllocationException { - CallContext.current().setEventDetails("Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getId())); + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.ID)); ServiceOffering serviceOffering = _entityMgr.findById(ServiceOffering.class, serviceOfferingId); if (serviceOffering == null) { @@ -153,7 +153,7 @@ public void execute() throws ResourceAllocationException { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to upgrade vm"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to upgrade Instance"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vmgroup/CreateVMGroupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vmgroup/CreateVMGroupCmd.java index e2952b5bd818..12f534e1a221 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vmgroup/CreateVMGroupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vmgroup/CreateVMGroupCmd.java @@ -30,7 +30,7 @@ import com.cloud.vm.InstanceGroup; -@APICommand(name = "createInstanceGroup", description = "Creates a vm group", responseObject = InstanceGroupResponse.class, entityType = {InstanceGroup.class}, +@APICommand(name = "createInstanceGroup", description = "Creates an Instance group", responseObject = InstanceGroupResponse.class, entityType = {InstanceGroup.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateVMGroupCmd extends BaseCmd { @@ -39,21 +39,21 @@ public class CreateVMGroupCmd extends BaseCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the instance group") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "The name of the Instance group") private String groupName; @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, - description = "the account of the instance group. The account parameter must be used with the domainId parameter.") + description = "The account of the Instance group. The account parameter must be used with the domainId parameter.") private String accountName; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "the domain ID of account owning the instance group") + description = "The domain ID of account owning the Instance group") private Long domainId; - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "The project of the instance group") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "The project of the Instance group") private Long projectId; // /////////////////////////////////////////////////// @@ -82,7 +82,7 @@ public Long getProjectId() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } @@ -98,7 +98,7 @@ public void execute() { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create vm instance group"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Instance Instance group"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vmgroup/DeleteVMGroupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vmgroup/DeleteVMGroupCmd.java index b74bc43eeb7a..b07084a273c5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vmgroup/DeleteVMGroupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vmgroup/DeleteVMGroupCmd.java @@ -31,7 +31,7 @@ import com.cloud.user.Account; import com.cloud.vm.InstanceGroup; -@APICommand(name = "deleteInstanceGroup", description = "Deletes a vm group", responseObject = SuccessResponse.class, entityType = {InstanceGroup.class}, +@APICommand(name = "deleteInstanceGroup", description = "Deletes an Instance group", responseObject = SuccessResponse.class, entityType = {InstanceGroup.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DeleteVMGroupCmd extends BaseCmd { @@ -40,7 +40,7 @@ public class DeleteVMGroupCmd extends BaseCmd { ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = InstanceGroupResponse.class, required = true, description = "the ID of the instance group") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = InstanceGroupResponse.class, required = true, description = "The ID of the Instance group") private Long id; ///////////////////////////////////////////////////// @@ -72,7 +72,7 @@ public void execute() { SuccessResponse response = new SuccessResponse(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete vm group"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete Instance group"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vmgroup/ListVMGroupsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vmgroup/ListVMGroupsCmd.java index 31845a956e92..ee918944046f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vmgroup/ListVMGroupsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vmgroup/ListVMGroupsCmd.java @@ -26,7 +26,7 @@ import com.cloud.vm.InstanceGroup; -@APICommand(name = "listInstanceGroups", description = "Lists vm groups", responseObject = InstanceGroupResponse.class, entityType = {InstanceGroup.class}, +@APICommand(name = "listInstanceGroups", description = "Lists Instance groups", responseObject = InstanceGroupResponse.class, entityType = {InstanceGroup.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListVMGroupsCmd extends BaseListProjectAndAccountResourcesCmd { @@ -35,10 +35,10 @@ public class ListVMGroupsCmd extends BaseListProjectAndAccountResourcesCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = InstanceGroupResponse.class, description = "list instance groups by ID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = InstanceGroupResponse.class, description = "List Instance groups by ID") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "list instance groups by name") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "List Instance groups by name") private String groupName; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vmgroup/UpdateVMGroupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vmgroup/UpdateVMGroupCmd.java index 5c553f064042..d63a5aea6dea 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vmgroup/UpdateVMGroupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vmgroup/UpdateVMGroupCmd.java @@ -30,7 +30,7 @@ import com.cloud.user.Account; import com.cloud.vm.InstanceGroup; -@APICommand(name = "updateInstanceGroup", description = "Updates a vm group", responseObject = InstanceGroupResponse.class, entityType = {InstanceGroup.class}, +@APICommand(name = "updateInstanceGroup", description = "Updates an Instance group", responseObject = InstanceGroupResponse.class, entityType = {InstanceGroup.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class UpdateVMGroupCmd extends BaseCmd { @@ -42,7 +42,7 @@ public class UpdateVMGroupCmd extends BaseCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = InstanceGroupResponse.class, required = true, description = "Instance group ID") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "new instance group name") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "New Instance group name") private String groupName; ///////////////////////////////////////////////////// @@ -79,7 +79,7 @@ public void execute() { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update vm instance group"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update Instance group"); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vmsnapshot/CreateVMSnapshotCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vmsnapshot/CreateVMSnapshotCmd.java index 18a478e9daec..d3128599b616 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vmsnapshot/CreateVMSnapshotCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vmsnapshot/CreateVMSnapshotCmd.java @@ -34,28 +34,27 @@ import com.cloud.event.EventTypes; import com.cloud.exception.ResourceAllocationException; import com.cloud.uservm.UserVm; -import com.cloud.vm.VirtualMachine; import com.cloud.vm.snapshot.VMSnapshot; -@APICommand(name = "createVMSnapshot", description = "Creates snapshot for a vm.", responseObject = VMSnapshotResponse.class, since = "4.2.0", entityType = {VMSnapshot.class}, +@APICommand(name = "createVMSnapshot", description = "Creates Snapshot for an Instance.", responseObject = VMSnapshotResponse.class, since = "4.2.0", entityType = {VMSnapshot.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateVMSnapshotCmd extends BaseAsyncCreateCmd { @ACL(accessType = AccessType.OperateEntry) - @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, required = true, entityType = UserVmResponse.class, description = "The ID of the vm") + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, required = true, entityType = UserVmResponse.class, description = "The ID of the Instance") private Long vmId; - @Parameter(name = ApiConstants.VM_SNAPSHOT_DESCRIPTION, type = CommandType.STRING, required = false, description = "The description of the snapshot") + @Parameter(name = ApiConstants.VM_SNAPSHOT_DESCRIPTION, type = CommandType.STRING, required = false, description = "The description of the Snapshot") private String description; - @Parameter(name = ApiConstants.VM_SNAPSHOT_DISPLAYNAME, type = CommandType.STRING, required = false, description = "The display name of the snapshot") + @Parameter(name = ApiConstants.VM_SNAPSHOT_DISPLAYNAME, type = CommandType.STRING, required = false, description = "The display name of the Snapshot") private String displayName; - @Parameter(name = ApiConstants.VM_SNAPSHOT_MEMORY, type = CommandType.BOOLEAN, required = false, description = "snapshot memory if true") + @Parameter(name = ApiConstants.VM_SNAPSHOT_MEMORY, type = CommandType.BOOLEAN, required = false, description = "Snapshot memory if true") private Boolean snapshotMemory; - @Parameter(name = ApiConstants.VM_SNAPSHOT_QUIESCEVM, type = CommandType.BOOLEAN, required = false, description = "quiesce vm if true") + @Parameter(name = ApiConstants.VM_SNAPSHOT_QUIESCEVM, type = CommandType.BOOLEAN, required = false, description = "Quiesce Instance if true") private Boolean quiescevm; public Boolean snapshotMemory() { @@ -92,20 +91,20 @@ public void create() throws ResourceAllocationException { try { vmsnapshot = _vmSnapshotService.allocVMSnapshot(getVmId(), getDisplayName(), getDescription(), snapshotMemory()); } catch (CloudRuntimeException e) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create vm snapshot: " + e.getMessage(), e); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Instance Snapshot: " + e.getMessage(), e); } if (vmsnapshot != null) { setEntityId(vmsnapshot.getId()); setEntityUuid(vmsnapshot.getUuid()); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create vm snapshot"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Instance Snapshot"); } } @Override public String getEventDescription() { - return "creating snapshot for VM: " + this._uuidMgr.getUuid(VirtualMachine.class, getVmId()); + return "Creating Snapshot for Instance: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID); } @Override @@ -115,14 +114,14 @@ public String getEventType() { @Override public void execute() { - CallContext.current().setEventDetails("VM Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getVmId())); + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID)); VMSnapshot result = _vmSnapshotService.createVMSnapshot(getVmId(), getEntityId(), getQuiescevm()); if (result != null) { VMSnapshotResponse response = _responseGenerator.createVMSnapshotResponse(result); response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create vm snapshot due to an internal error creating snapshot for vm " + getVmId()); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Instance Snapshot due to an internal error creating Snapshot for Instance " + getVmId()); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vmsnapshot/DeleteVMSnapshotCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vmsnapshot/DeleteVMSnapshotCmd.java index 94b8824f8685..3373ac534cc8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vmsnapshot/DeleteVMSnapshotCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vmsnapshot/DeleteVMSnapshotCmd.java @@ -35,7 +35,7 @@ import com.cloud.user.Account; import com.cloud.vm.snapshot.VMSnapshot; -@APICommand(name = "deleteVMSnapshot", description = "Deletes a vmsnapshot.", responseObject = SuccessResponse.class, since = "4.2.0", entityType = {VMSnapshot.class}, +@APICommand(name = "deleteVMSnapshot", description = "Deletes an Instance Snapshot.", responseObject = SuccessResponse.class, since = "4.2.0", entityType = {VMSnapshot.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DeleteVMSnapshotCmd extends BaseAsyncCmd { @@ -44,7 +44,7 @@ public class DeleteVMSnapshotCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = VMSnapshotResponse.class, required = true, - description = "The ID of the VM snapshot") + description = "The ID of the Instance Snapshot") private Long id; public Long getId() { @@ -62,19 +62,19 @@ public long getEntityOwnerId() { @Override public void execute() { - CallContext.current().setEventDetails("vmsnapshot id: " + this._uuidMgr.getUuid(VMSnapshot.class, getId())); + CallContext.current().setEventDetails("Instance Snapshot ID: " + getResourceUuid(ApiConstants.VM_SNAPSHOT_ID)); boolean result = _vmSnapshotService.deleteVMSnapshot(getId()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete vm snapshot"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete Instance Snapshot"); } } @Override public String getEventDescription() { - return "Delete VM snapshot: " + this._uuidMgr.getUuid(VMSnapshot.class, getId()); + return "Deleting Instance Snapshot with ID: " + getResourceUuid(ApiConstants.VM_SNAPSHOT_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vmsnapshot/ListVMSnapshotCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vmsnapshot/ListVMSnapshotCmd.java index 87dd6e0b4789..b5d603e000c1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vmsnapshot/ListVMSnapshotCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vmsnapshot/ListVMSnapshotCmd.java @@ -31,24 +31,24 @@ import com.cloud.utils.Pair; import com.cloud.vm.snapshot.VMSnapshot; -@APICommand(name = "listVMSnapshot", description = "List virtual machine snapshot by conditions", responseObject = VMSnapshotResponse.class, since = "4.2.0", entityType = {VMSnapshot.class}, +@APICommand(name = "listVMSnapshot", description = "List Instance Snapshot by conditions", responseObject = VMSnapshotResponse.class, since = "4.2.0", entityType = {VMSnapshot.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListVMSnapshotCmd extends BaseListTaggedResourcesCmd { - @Parameter(name = ApiConstants.VM_SNAPSHOT_ID, type = CommandType.UUID, entityType = VMSnapshotResponse.class, description = "The ID of the VM snapshot") + @Parameter(name = ApiConstants.VM_SNAPSHOT_ID, type = CommandType.UUID, entityType = VMSnapshotResponse.class, description = "The ID of the Instance Snapshot") private Long id; - @Parameter(name=ApiConstants.VM_SNAPSHOT_IDS, type=CommandType.LIST, collectionType=CommandType.UUID, entityType=VMSnapshotResponse.class, description="the IDs of the vm snapshots, mutually exclusive with vmsnapshotid", since = "4.9") + @Parameter(name=ApiConstants.VM_SNAPSHOT_IDS, type=CommandType.LIST, collectionType=CommandType.UUID, entityType=VMSnapshotResponse.class, description = "The IDs of the Instance Snapshots, mutually exclusive with vmsnapshotid", since = "4.9") private List ids; - @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "state of the virtual machine snapshot") + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "State of the Instance Snapshot") private String state; - @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, description = "the ID of the vm") + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, description = "The ID of the Instance") private Long vmId; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "lists snapshot by snapshot name or display name") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Lists Snapshot by Snapshot name or display name") private String vmSnapshotName; public String getState() { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vmsnapshot/RevertToVMSnapshotCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vmsnapshot/RevertToVMSnapshotCmd.java index 310b45687d49..d44cefca5027 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vmsnapshot/RevertToVMSnapshotCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vmsnapshot/RevertToVMSnapshotCmd.java @@ -41,7 +41,7 @@ import com.cloud.uservm.UserVm; import com.cloud.vm.snapshot.VMSnapshot; -@APICommand(name = "revertToVMSnapshot", description = "Revert VM from a vmsnapshot.", responseObject = UserVmResponse.class, since = "4.2.0", responseView = ResponseView.Restricted, +@APICommand(name = "revertToVMSnapshot", description = "Revert Instance from a vmsnapshot.", responseObject = UserVmResponse.class, since = "4.2.0", responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class RevertToVMSnapshotCmd extends BaseAsyncCmd implements UserCmd { private static final String s_name = "reverttovmsnapshotresponse"; @@ -51,7 +51,7 @@ public class RevertToVMSnapshotCmd extends BaseAsyncCmd implements UserCmd { type = CommandType.UUID, required = true, entityType = VMSnapshotResponse.class, - description = "The ID of the vm snapshot") + description = "The ID of the Instance Snapshot") private Long vmSnapShotId; public Long getVmSnapShotId() { @@ -74,7 +74,7 @@ public long getEntityOwnerId() { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, ConcurrentOperationException { - CallContext.current().setEventDetails("vmsnapshot id: " + this._uuidMgr.getUuid(VMSnapshot.class, getVmSnapShotId())); + CallContext.current().setEventDetails("Instance Snapshot ID: " + getResourceUuid(ApiConstants.VM_SNAPSHOT_ID)); UserVm result = _vmSnapshotService.revertToSnapshot(getVmSnapShotId()); if (result != null) { UserVmResponse response = _responseGenerator.createUserVmResponse(getResponseView(), @@ -82,13 +82,13 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacity response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to revert VM snapshot"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to revert Instance Snapshot"); } } @Override public String getEventDescription() { - return "Revert from VM snapshot: " + this._uuidMgr.getUuid(VMSnapshot.class, getVmSnapShotId()); + return "Reverting from Instance Snapshot with ID: " + getResourceUuid(ApiConstants.VM_SNAPSHOT_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/AddResourceDetailCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/AddResourceDetailCmd.java index 287991fa9846..9e1c1056fdf9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/AddResourceDetailCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/AddResourceDetailCmd.java @@ -40,13 +40,13 @@ public class AddResourceDetailCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, required = true, description = "Map of (key/value pairs)") private Map details; - @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, required = true, description = "type of the resource") + @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, required = true, description = "Type of the resource") private String resourceType; - @Parameter(name = ApiConstants.RESOURCE_ID, type = CommandType.STRING, required = true, collectionType = CommandType.STRING, description = "resource id to create the details for") + @Parameter(name = ApiConstants.RESOURCE_ID, type = CommandType.STRING, required = true, collectionType = CommandType.STRING, description = "Resource ID to create the details for") private String resourceId; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "pass false if you want this detail to be disabled for the regular user. True by default", since = "4.4") + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "Pass false if you want this detail to be disabled for the regular User. True by default", since = "4.4") private Boolean display; ///////////////////////////////////////////////////// @@ -90,7 +90,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "adding details to the resource "; + return "Adding details to the resource "; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/AssignVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/AssignVolumeCmd.java index 1a51aa03c226..f50abaf73c96 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/AssignVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/AssignVolumeCmd.java @@ -34,7 +34,7 @@ import java.util.Map; -@APICommand(name = AssignVolumeCmd.CMD_NAME, responseObject = VolumeResponse.class, description = "Changes ownership of a Volume from one account to another.", entityType = { +@APICommand(name = AssignVolumeCmd.CMD_NAME, responseObject = VolumeResponse.class, description = "Changes ownership of a Volume from one Account to another.", entityType = { Volume.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.18.0.0") public class AssignVolumeCmd extends BaseCmd implements UserCmd { public static final String CMD_NAME = "assignVolume"; @@ -47,7 +47,7 @@ public class AssignVolumeCmd extends BaseCmd implements UserCmd { private Long volumeId; @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, - description = "The ID of the account to which the volume will be assigned. Mutually exclusive with parameter 'projectid'.") + description = "The ID of the Account to which the volume will be assigned. Mutually exclusive with parameter 'projectid'.") private Long accountId; @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, @@ -70,6 +70,21 @@ public Long getProjectid() { return projectid; } + ///////////////////////////////////////////////////// + /////////////////// Setter/////////////////////////// + ///////////////////////////////////////////////////// + public void setVolumeId(Long volumeId) { + this.volumeId = volumeId; + } + + public void setAccountId(Long accountId) { + this.accountId = accountId; + } + + public void setProjectId(Long projectid) { + this.projectid = projectid; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -81,7 +96,7 @@ public void execute() { if (result == null) { Map fullParams = getFullUrlParams(); if (accountId != null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to move volume [%s] to account [%s].", fullParams.get(ApiConstants.VOLUME_ID), + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to move volume [%s] to Account [%s].", fullParams.get(ApiConstants.VOLUME_ID), fullParams.get(ApiConstants.ACCOUNT_ID))); } throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to move volume [%s] to project [%s].", fullParams.get(ApiConstants.VOLUME_ID), diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/AttachVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/AttachVolumeCmd.java index 1a3b9220877f..8624043afc51 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/AttachVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/AttachVolumeCmd.java @@ -37,7 +37,7 @@ import com.cloud.user.Account; import com.cloud.vm.VirtualMachine; -@APICommand(name = "attachVolume", description = "Attaches a disk volume to a virtual machine.", responseObject = VolumeResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, +@APICommand(name = "attachVolume", description = "Attaches a disk volume to an Instance.", responseObject = VolumeResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class AttachVolumeCmd extends BaseAsyncCmd implements UserCmd { private static final String s_name = "attachvolumeresponse"; @@ -52,12 +52,12 @@ public class AttachVolumeCmd extends BaseAsyncCmd implements UserCmd { + "Please refer to the docs of your hypervisor for the correct mapping of the deviceID and the actual logical disk structure.") private Long deviceId; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VolumeResponse.class, required = true, description = "the ID of the disk volume") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VolumeResponse.class, required = true, description = "The ID of the disk volume") private Long id; @ACL(accessType = AccessType.OperateEntry) @Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.UUID, entityType=UserVmResponse.class, - required=true, description=" the ID of the virtual machine") + required=true, description = " the ID of the Instance") private Long virtualMachineId; ///////////////////////////////////////////////////// @@ -111,12 +111,12 @@ public String getEventType() { @Override public String getEventDescription() { - return "attaching volume: " + this._uuidMgr.getUuid(Volume.class, getId()) + " to vm: " + this._uuidMgr.getUuid(VirtualMachine.class, getVirtualMachineId()); + return "Attaching volume with ID: " + getResourceUuid(ApiConstants.ID) + " to Instance with ID: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID); } @Override public void execute() { - CallContext.current().setEventDetails("Volume Id: " + this._uuidMgr.getUuid(Volume.class, getId()) + " VmId: " + this._uuidMgr.getUuid(VirtualMachine.class, getVirtualMachineId())); + CallContext.current().setEventDetails("Volume ID: " + getResourceUuid(ApiConstants.ID) + " Instance ID: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID)); Volume result = _volumeService.attachVolumeToVM(this); if (result != null) { VolumeResponse response = _responseGenerator.createVolumeResponse(getResponseView(), result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ChangeOfferingForVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ChangeOfferingForVolumeCmd.java index 404a02b04b8b..c8cda7e1c19c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ChangeOfferingForVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ChangeOfferingForVolumeCmd.java @@ -22,7 +22,6 @@ import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; -import com.cloud.offering.DiskOffering; import com.cloud.storage.Volume; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; @@ -50,14 +49,14 @@ public class ChangeOfferingForVolumeCmd extends BaseAsyncCmd implements UserCmd //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, entityType = VolumeResponse.class, required = true, type = CommandType.UUID, description = "the ID of the volume") + @Parameter(name = ApiConstants.ID, entityType = VolumeResponse.class, required = true, type = CommandType.UUID, description = "The ID of the volume") private Long id; @Parameter(name = ApiConstants.DISK_OFFERING_ID, entityType = DiskOfferingResponse.class, type = CommandType.UUID, required = true, - description = "new disk offering id") + description = "New disk offering ID") private Long newDiskOfferingId; @Parameter(name = ApiConstants.SIZE, type = CommandType.LONG, required = false, description = "New volume size in GB for the custom disk offering") @@ -130,12 +129,12 @@ public String getEventType() { @Override public String getEventDescription() { - return "Changing Disk offering of Volume Id: " + this._uuidMgr.getUuid(Volume.class, getId()) + " to " + this._uuidMgr.getUuid(DiskOffering.class, getNewDiskOfferingId()); + return "Changing disk offering of volume with ID: " + getResourceUuid(ApiConstants.ID) + " to " + getResourceUuid(ApiConstants.DISK_OFFERING_ID); } @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { - CallContext.current().setEventDetails("Volume Id: " + getId()); + CallContext.current().setEventDetails("Volume ID: " + getResourceUuid(ApiConstants.ID)); Volume result = _volumeService.changeDiskOfferingForVolume(this); if (result != null) { VolumeResponse response = _responseGenerator.createVolumeResponse(ResponseObject.ResponseView.Restricted, result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/CheckAndRepairVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/CheckAndRepairVolumeCmd.java index 56fdf6bc126c..fdbd4a61c072 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/CheckAndRepairVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/CheckAndRepairVolumeCmd.java @@ -105,7 +105,7 @@ public String getEventType() { @Override public String getEventDescription() { - return String.format("check and repair operation on volume: %s", this._uuidMgr.getUuid(Volume.class, getId())); + return "Starting checking and repairing operation on volume: " + getResourceUuid(ApiConstants.ID); } @Override @@ -120,7 +120,7 @@ public ApiCommandResourceType getApiResourceType() { @Override public void execute() throws ResourceAllocationException { - CallContext.current().setEventDetails("Volume Id: " + getId()); + CallContext.current().setEventDetails("Volume ID: " + getResourceUuid(ApiConstants.ID)); Pair result = _volumeService.checkAndRepairVolume(this); Volume volume = _responseGenerator.findVolumeById(getId()); if (result != null) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/CreateVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/CreateVolumeCmd.java index 7ffcea50b219..538e263ae9de 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/CreateVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/CreateVolumeCmd.java @@ -30,8 +30,10 @@ 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.KMSKeyResponse; import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.cloudstack.api.response.SnapshotResponse; +import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.VolumeResponse; import org.apache.cloudstack.api.response.ZoneResponse; @@ -43,7 +45,7 @@ import com.cloud.storage.Volume; import com.cloud.vm.VirtualMachine; -@APICommand(name = "createVolume", responseObject = VolumeResponse.class, description = "Creates a disk volume from a disk offering. This disk volume must still be attached to a virtual machine to make use of it.", responseView = ResponseView.Restricted, entityType = { +@APICommand(name = "createVolume", responseObject = VolumeResponse.class, description = "Creates a disk volume from a disk offering. This disk volume must still be attached to an Instance to make use of it.", responseView = ResponseView.Restricted, entityType = { Volume.class, VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateVolumeCmd extends BaseAsyncCreateCustomIdCmd implements UserCmd { @@ -55,60 +57,75 @@ public class CreateVolumeCmd extends BaseAsyncCreateCustomIdCmd implements UserC @Parameter(name = ApiConstants.ACCOUNT, type = BaseCmd.CommandType.STRING, - description = "the account associated with the disk volume. Must be used with the domainId parameter.") + description = "The Account associated with the disk volume. Must be used with the domainId parameter.") private String accountName; @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, - description = "the project associated with the volume. Mutually exclusive with account parameter") + description = "The project associated with the volume. Mutually exclusive with Account parameter") private Long projectId; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "the domain ID associated with the disk offering. If used with the account parameter" - + " returns the disk volume associated with the account for the specified domain." + - "If account is NOT provided then the volume will be assigned to the caller account and domain.") + description = "The domain ID associated with the disk offering. If used with the Account parameter" + + " returns the disk volume associated with the Account for the specified domain." + + "If account is NOT provided then the volume will be assigned to the caller Account and domain.") private Long domainId; @Parameter(name = ApiConstants.DISK_OFFERING_ID, required = false, type = CommandType.UUID, entityType = DiskOfferingResponse.class, - description = "the ID of the disk offering. Either diskOfferingId or snapshotId must be passed in.") + description = "The ID of the disk offering. Either diskOfferingId or snapshotId must be passed in.") private Long diskOfferingId; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the disk volume") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the disk volume") private String volumeName; @Parameter(name = ApiConstants.SIZE, type = CommandType.LONG, description = "Arbitrary volume size") private Long size; - @Parameter(name = ApiConstants.MIN_IOPS, type = CommandType.LONG, description = "min iops") + @Parameter(name = ApiConstants.MIN_IOPS, type = CommandType.LONG, description = "Min IOPS") private Long minIops; - @Parameter(name = ApiConstants.MAX_IOPS, type = CommandType.LONG, description = "max iops") + @Parameter(name = ApiConstants.MAX_IOPS, type = CommandType.LONG, description = "Max IOPS") private Long maxIops; @Parameter(name = ApiConstants.SNAPSHOT_ID, type = CommandType.UUID, entityType = SnapshotResponse.class, - description = "the snapshot ID for the disk volume. Either diskOfferingId or snapshotId must be passed in.") + description = "The Snapshot ID for the disk volume. Either diskOfferingId or snapshotId must be passed in.") private Long snapshotId; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the ID of the availability zone") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The ID of the availability zone") private Long zoneId; - @Parameter(name = ApiConstants.DISPLAY_VOLUME, type = CommandType.BOOLEAN, description = "an optional field, whether to display the volume to the end user or not.", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.DISPLAY_VOLUME, type = CommandType.BOOLEAN, description = "An optional field, whether to display the volume to the end User or not.", authorized = {RoleType.Admin}) private Boolean displayVolume; @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, - description = "the ID of the virtual machine; to be used with snapshot Id, VM to which the volume gets attached after creation") + description = "The ID of the Instance; to be used with snapshot Id, Instance to which the volume gets attached after creation") private Long virtualMachineId; + @Parameter(name = ApiConstants.KMS_KEY_ID, + type = CommandType.UUID, + entityType = KMSKeyResponse.class, + description = "ID of the KMS Key for volume encryption (required if encryption enabled for zone)", + since = "4.23.0") + private Long kmsKeyId; + + @Parameter(name = ApiConstants.STORAGE_ID, + type = CommandType.UUID, + entityType = StoragePoolResponse.class, + description = "Storage pool ID to create the volume in. Cannot be used with the snapshotid parameter.", + authorized = {RoleType.Admin}, + since = "4.22.1") + private Long storageId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -142,6 +159,10 @@ public Long getMaxIops() { } public Long getSnapshotId() { + if (storageId != null && snapshotId != null) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, + "Snapshot ID cannot be specified with the Storage ID."); + } return snapshotId; } @@ -153,6 +174,14 @@ private Long getProjectId() { return projectId; } + public Long getStorageId() { + if (snapshotId != null && storageId != null) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, + "Storage ID cannot be specified with the Snapshot ID."); + } + return storageId; + } + public Boolean getDisplayVolume() { return displayVolume; } @@ -169,6 +198,10 @@ public Long getVirtualMachineId() { return virtualMachineId; } + public Long getKmsKeyId() { + return kmsKeyId; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -188,7 +221,7 @@ public ApiCommandResourceType getApiResourceType() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } @@ -203,7 +236,17 @@ public String getEventType() { @Override public String getEventDescription() { - return "creating volume: " + getVolumeName() + ((getSnapshotId() == null) ? "" : " from snapshot: " + this._uuidMgr.getUuid(Snapshot.class, getSnapshotId())); + String description = "Creating volume "; + + if (getVolumeName() != null) { + description += getVolumeName(); + } + + if (getSnapshotId() != null) { + description += " from Snapshot: " + getResourceUuid(ApiConstants.SNAPSHOT_ID); + } + + return description; } @Override @@ -220,7 +263,7 @@ public void create() throws ResourceAllocationException { @Override public void execute() { - CallContext.current().setEventDetails("Volume Id: " + getEntityUuid() + ((getSnapshotId() == null) ? "" : " from snapshot: " + this._uuidMgr.getUuid(Snapshot.class, getSnapshotId()))); + CallContext.current().setEventDetails("Volume ID: " + getEntityUuid() + ((getSnapshotId() == null) ? "" : " from Snapshot with ID: " + getResourceUuid(ApiConstants.SNAPSHOT_ID))); Volume volume = _volumeService.createVolume(this); if (volume != null) { VolumeResponse response = _responseGenerator.createVolumeResponse(getResponseView(), volume); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/DeleteVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/DeleteVolumeCmd.java index 6111488a8021..ab6cda651b06 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/DeleteVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/DeleteVolumeCmd.java @@ -43,7 +43,7 @@ public class DeleteVolumeCmd extends BaseCmd { @ACL(accessType = AccessType.OperateEntry) @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType=VolumeResponse.class, - required=true, description="The ID of the disk volume") + required=true, description = "The ID of the disk volume") private Long id; ///////////////////////////////////////////////////// @@ -84,8 +84,8 @@ public ApiCommandResourceType getApiResourceType() { @Override public void execute() throws ConcurrentOperationException { - CallContext.current().setEventDetails("Volume Id: " + this._uuidMgr.getUuid(Volume.class, getId())); - Volume result = _volumeService.destroyVolume(id, CallContext.current().getCallingAccount(), true, false); + CallContext.current().setEventDetails("Volume ID: " + getResourceUuid(ApiConstants.ID)); + Volume result = _volumeService.destroyVolume(id, CallContext.current().getCallingAccount(), true, false, null); if (result != null) { SuccessResponse response = new SuccessResponse(getCommandName()); setResponseObject(response); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/DestroyVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/DestroyVolumeCmd.java index 2eafb76e5915..e9e388436642 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/DestroyVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/DestroyVolumeCmd.java @@ -50,7 +50,7 @@ public class DestroyVolumeCmd extends BaseAsyncCmd { @ACL(accessType = AccessType.OperateEntry) @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType=VolumeResponse.class, - required=true, description="The ID of the volume") + required=true, description = "The ID of the volume") private Long id; @Parameter(name = ApiConstants.EXPUNGE, @@ -100,7 +100,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "destroying volume: " + getId(); + return "Destroying volume with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -115,8 +115,8 @@ public Long getApiResourceId() { @Override public void execute() { - CallContext.current().setEventDetails("Volume Id: " + getId()); - Volume result = _volumeService.destroyVolume(getId(), CallContext.current().getCallingAccount(), getExpunge(), false); + CallContext.current().setEventDetails("Volume ID: " + getResourceUuid(ApiConstants.ID)); + Volume result = _volumeService.destroyVolume(getId(), CallContext.current().getCallingAccount(), getExpunge(), false, null); if (result != null) { VolumeResponse response = _responseGenerator.createVolumeResponse(ResponseView.Restricted, result); response.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/DetachVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/DetachVolumeCmd.java index 2fddcace84dd..f6e811da6055 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/DetachVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/DetachVolumeCmd.java @@ -38,7 +38,7 @@ import com.cloud.uservm.UserVm; import com.cloud.vm.VirtualMachine; -@APICommand(name = "detachVolume", description = "Detaches a disk volume from a virtual machine.", responseObject = VolumeResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, +@APICommand(name = "detachVolume", description = "Detaches a disk volume from an Instance.", responseObject = VolumeResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DetachVolumeCmd extends BaseAsyncCmd implements UserCmd { private static final String s_name = "detachvolumeresponse"; @@ -48,17 +48,17 @@ public class DetachVolumeCmd extends BaseAsyncCmd implements UserCmd { ///////////////////////////////////////////////////// @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType=VolumeResponse.class, - description="the ID of the disk volume") + description= " The ID of the disk volume") private Long id; - @Parameter(name = ApiConstants.DEVICE_ID, type = CommandType.LONG, description = "the device ID on the virtual machine where volume is detached from") + @Parameter(name = ApiConstants.DEVICE_ID, type = CommandType.LONG, description = "The device ID on the Instance where volume is detached from") private Long deviceId; @ACL(accessType = AccessType.OperateEntry) @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, - description = "the ID of the virtual machine where the volume is detached from") + description = "The ID of the Instance where the volume is detached from") private Long virtualMachineId; ///////////////////////////////////////////////////// @@ -77,6 +77,10 @@ public Long getVirtualMachineId() { return virtualMachineId; } + public void setId(Long id) { + this.id = id; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -126,15 +130,19 @@ public String getEventType() { @Override public String getEventDescription() { - StringBuilder sb = new StringBuilder(); + String description = "Detaching volume"; + if (id != null) { - sb.append(": " + this._uuidMgr.getUuid(Volume.class, id)); - } else if ((deviceId != null) && (virtualMachineId != null)) { - sb.append(" with device id: " + deviceId + " from vm: " + ((getVirtualMachineId() != null) ? this._uuidMgr.getUuid(VirtualMachine.class, getVirtualMachineId()) : "" )); + description += ": " + getResourceUuid(ApiConstants.ID); + } + + if ((deviceId != null) && (virtualMachineId != null)) { + description += " with device id: " + deviceId + " from Instance: " + getResourceUuid(ApiConstants.VIRTUAL_MACHINE_ID); } else { - sb.append(" "); + description += " "; } - return "detaching volume" + sb.toString(); + + return description; } @Override 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 6ddb5f1e567b..f50d23a6b60c 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 @@ -16,8 +16,6 @@ // under the License. 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; @@ -47,20 +45,20 @@ public class ExtractVolumeCmd extends BaseAsyncCmd { @ACL(accessType = AccessType.OperateEntry) @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType=VolumeResponse.class, - required=true, description="the ID of the volume") + required=true, description = "The ID of the volume") private Long id; - @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = false, length = 2048, description = "the url to which the volume would be extracted") + @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = false, length = 2048, description = "The URL to which the volume would be extracted") private String url; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, - description = "the ID of the zone where the volume is located") + description = "The ID of the zone where the volume is located") private Long zoneId; - @Parameter(name = ApiConstants.MODE, type = CommandType.STRING, required = true, description = "the mode of extraction - HTTP_DOWNLOAD or FTP_UPLOAD") + @Parameter(name = ApiConstants.MODE, type = CommandType.STRING, required = true, description = "The mode of extraction - HTTP_DOWNLOAD or FTP_UPLOAD") private String mode; ///////////////////////////////////////////////////// @@ -115,10 +113,7 @@ public String getEventType() { @Override public String getEventDescription() { - 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); + return "Extracting volume with ID: " + getResourceUuid(ApiConstants.ID) + " from zone with ID: " + getResourceUuid(ApiConstants.ZONE_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/GetUploadParamsForVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/GetUploadParamsForVolumeCmd.java index 4ccd5f97993a..1e3b2e460772 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/GetUploadParamsForVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/GetUploadParamsForVolumeCmd.java @@ -40,7 +40,7 @@ public class GetUploadParamsForVolumeCmd extends AbstractGetUploadParamsCmd { @Parameter(name = ApiConstants.IMAGE_STORE_UUID, type = CommandType.STRING, description = "Image store uuid") private String imageStoreUuid; - @Parameter(name = ApiConstants.DISK_OFFERING_ID, required = false, type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "the ID of the disk " + @Parameter(name = ApiConstants.DISK_OFFERING_ID, required = false, type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "The ID of the disk " + "offering. This must be a custom sized offering since during upload of volume/template size is unknown.") private Long diskOfferingId; @@ -72,7 +72,7 @@ public String getCommandName() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(getAccountName(), getDomainId(), getProjectId(), true); + Long accountId = _accountService.finalizeAccountId(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/volume/ListResourceDetailsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ListResourceDetailsCmd.java index 318836d8d877..93a2445ee8f3 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ListResourceDetailsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ListResourceDetailsCmd.java @@ -34,20 +34,20 @@ requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListResourceDetailsCmd extends BaseListProjectAndAccountResourcesCmd { - @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, description = "list by resource type", required = true) + @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, description = "List by resource type", required = true) private String resourceType; - @Parameter(name = ApiConstants.RESOURCE_ID, type = CommandType.STRING, description = "list by resource id") + @Parameter(name = ApiConstants.RESOURCE_ID, type = CommandType.STRING, description = "List by resource ID") private String resourceId; - @Parameter(name = ApiConstants.KEY, type = CommandType.STRING, description = "list by key") + @Parameter(name = ApiConstants.KEY, type = CommandType.STRING, description = "List by key") private String key; - @Parameter(name = ApiConstants.VALUE, type = CommandType.STRING, description = "list by key, value. Needs to be passed only along with key" , + @Parameter(name = ApiConstants.VALUE, type = CommandType.STRING, description = "List by key, value. Needs to be passed only along with key" , since = "4.4", authorized = { RoleType.Admin }) private String value; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "if set to true, only details marked with display=true, are returned." + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "If set to true, only details marked with display=true, are returned." + " False by default", since = "4.3", authorized = { RoleType.Admin }) private Boolean forDisplay; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java index a1024a988981..88f87e941e78 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java @@ -29,6 +29,7 @@ import org.apache.cloudstack.api.response.ClusterResponse; import org.apache.cloudstack.api.response.DiskOfferingResponse; import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.KMSKeyResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.PodResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; @@ -50,55 +51,58 @@ public class ListVolumesCmd extends BaseListRetrieveOnlyResourceCountCmd impleme //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "list volumes on specified host") + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "List volumes on specified host") private Long hostId; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VolumeResponse.class, description = "the ID of the disk volume") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VolumeResponse.class, description = "The ID of the disk volume") private Long id; - @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = VolumeResponse.class, description = "the IDs of the volumes, mutually exclusive with id", since = "4.9") + @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = VolumeResponse.class, description = "The IDs of the volumes, mutually exclusive with id", since = "4.9") private List ids; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the disk volume") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the disk volume") private String volumeName; - @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "the pod id the disk volume belongs to") + @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "The pod ID the disk volume belongs to") private Long podId; - @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "the cluster id the disk volume belongs to", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "The cluster ID the disk volume belongs to", authorized = {RoleType.Admin}) private Long clusterId; - @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "the type of disk volume") + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "The type of disk volume") private String type; - @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, description = "the ID of the virtual machine") + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, description = "The ID of the Instance") private Long virtualMachineId; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the ID of the availability zone") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The ID of the availability zone") private Long zoneId; - @Parameter(name = ApiConstants.STORAGE_ID, type = CommandType.STRING, entityType = StoragePoolResponse.class, description = "the ID of the storage pool, available to ROOT admin only", since = "4.3", authorized = { + @Parameter(name = ApiConstants.STORAGE_ID, type = CommandType.STRING, entityType = StoragePoolResponse.class, description = "The ID of the storage pool, available to ROOT admin only", since = "4.3", authorized = { RoleType.Admin}) private String storageId; @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, - description = "list volumes by disk offering of a service offering. If both service offering and " + + description = "List volumes by disk offering of a service offering. If both service offering and " + "disk offering are passed, service offering is ignored", since = "4.19.1") private Long serviceOfferingId; - @Parameter(name = ApiConstants.DISK_OFFERING_ID, type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "list volumes by disk offering", since = "4.4") + @Parameter(name = ApiConstants.DISK_OFFERING_ID, type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "List volumes by disk offering", since = "4.4") private Long diskOfferingId; - @Parameter(name = ApiConstants.DISPLAY_VOLUME, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = { + @Parameter(name = ApiConstants.KMS_KEY_ID, type = CommandType.UUID, entityType = KMSKeyResponse.class, description = "List volumes by KMS Key", since = "4.23") + private Long kmsKeyId; + + @Parameter(name = ApiConstants.DISPLAY_VOLUME, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = { RoleType.Admin}) private Boolean display; - @Parameter(name = ApiConstants.LIST_SYSTEM_VMS, type = CommandType.BOOLEAN, description = "list system VMs; only ROOT admin is eligible to pass this parameter", since = "4.18", + @Parameter(name = ApiConstants.LIST_SYSTEM_VMS, type = CommandType.BOOLEAN, description = "List system VMs; only ROOT admin is eligible to pass this parameter", since = "4.18", authorized = { RoleType.Admin }) private Boolean listSystemVms; - @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "state of the volume. Possible values are: Ready, Allocated, Destroy, Expunging, Expunged.") + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "State of the volume. Possible values are: Ready, Allocated, Destroy, Expunging, Expunged.") private String state; @Parameter(name = ApiConstants.IS_ENCRYPTED, type = CommandType.BOOLEAN, description = "list only volumes that are encrypted", since = "4.19.1", @@ -136,6 +140,10 @@ public Long getDiskOfferingId() { return diskOfferingId; } + public Long getKmsKeyId() { + return kmsKeyId; + } + public String getType() { return type; } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/MigrateVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/MigrateVolumeCmd.java index a0a50c5b9fc5..9927978ad55e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/MigrateVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/MigrateVolumeCmd.java @@ -30,7 +30,6 @@ import org.apache.cloudstack.api.response.VolumeResponse; import com.cloud.event.EventTypes; -import com.cloud.storage.StoragePool; import com.cloud.storage.Volume; import com.cloud.user.Account; @@ -43,13 +42,13 @@ public class MigrateVolumeCmd extends BaseAsyncCmd implements UserCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.UUID, entityType = VolumeResponse.class, required = true, description = "the ID of the volume") + @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.UUID, entityType = VolumeResponse.class, required = true, description = "The ID of the volume") private Long volumeId; - @Parameter(name = ApiConstants.STORAGE_ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, required = true, description = "destination storage pool ID to migrate the volume to") + @Parameter(name = ApiConstants.STORAGE_ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, required = true, description = "Destination storage pool ID to migrate the volume to") private Long storageId; - @Parameter(name = ApiConstants.LIVE_MIGRATE, type = CommandType.BOOLEAN, required = false, description = "if the volume should be live migrated when it is attached to a running vm") + @Parameter(name = ApiConstants.LIVE_MIGRATE, type = CommandType.BOOLEAN, required = false, description = "If the volume should be live migrated when it is attached to a running Instance") private Boolean liveMigrate; @Parameter(name = ApiConstants.NEW_DISK_OFFERING_ID, type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "The new disk offering ID that replaces the current one used by the volume. This new disk offering is used to better reflect the new storage where the volume is going to be migrated to.") @@ -110,7 +109,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Attempting to migrate volume Id: " + this._uuidMgr.getUuid(Volume.class, getVolumeId()) + " to storage pool Id: " + this._uuidMgr.getUuid(StoragePool.class, getStoragePoolId()); + return "Attempting to migrate volume with ID: " + getResourceUuid(ApiConstants.VOLUME_ID) + " to storage pool: " + getResourceUuid(ApiConstants.STORAGE_ID); } public Long getNewDiskOfferingId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/RecoverVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/RecoverVolumeCmd.java index cd5a7735e382..4d6be7270afb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/RecoverVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/RecoverVolumeCmd.java @@ -87,7 +87,7 @@ public ApiCommandResourceType getApiResourceType() { @Override public void execute() { - CallContext.current().setEventDetails("Volume Id: " + getId()); + CallContext.current().setEventDetails("Volume ID: " + getResourceUuid(ApiConstants.ID)); Volume result = _volumeService.recoverVolume(getId()); if (result != null) { VolumeResponse response = _responseGenerator.createVolumeResponse(ResponseView.Full, result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ResizeVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ResizeVolumeCmd.java index eb89115cf464..f8f744285c04 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ResizeVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ResizeVolumeCmd.java @@ -51,7 +51,7 @@ public class ResizeVolumeCmd extends BaseAsyncCmd implements UserCmd { ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) - @Parameter(name = ApiConstants.ID, entityType = VolumeResponse.class, required = true, type = CommandType.UUID, description = "the ID of the disk volume") + @Parameter(name = ApiConstants.ID, entityType = VolumeResponse.class, required = true, type = CommandType.UUID, description = "The ID of the disk volume") private Long id; @Parameter(name = ApiConstants.MIN_IOPS, type = CommandType.LONG, required = false, description = "New minimum number of IOPS") @@ -70,7 +70,7 @@ public class ResizeVolumeCmd extends BaseAsyncCmd implements UserCmd { entityType = DiskOfferingResponse.class, type = CommandType.UUID, required = false, - description = "new disk offering id") + description = "New disk offering ID") private Long newDiskOfferingId; @Parameter(name = ApiConstants.AUTO_MIGRATE, type = CommandType.BOOLEAN, required = false, @@ -190,11 +190,13 @@ public String getEventType() { @Override public String getEventDescription() { + String baseDescription = "Resizing volume with ID: " + getResourceUuid(ApiConstants.ID); + if (getSize() != null) { - return "Volume Id: " + this._uuidMgr.getUuid(Volume.class, getEntityId()) + " to size " + getSize() + " GB"; - } else { - return "Volume Id: " + this._uuidMgr.getUuid(Volume.class, getEntityId()); + baseDescription = baseDescription + " to size " + getSize() + " GB."; } + + return baseDescription; } @Override @@ -202,9 +204,9 @@ public void execute() { Volume volume = null; try { if (size != null) { - CallContext.current().setEventDetails("Volume Id: " + this._uuidMgr.getUuid(Volume.class, getEntityId()) + " to size " + getSize() + " GB"); + CallContext.current().setEventDetails("Volume ID: " + getResourceUuid(ApiConstants.ID) + " to size " + getSize() + " GB"); } else { - CallContext.current().setEventDetails("Volume Id: " + this._uuidMgr.getUuid(Volume.class, getEntityId())); + CallContext.current().setEventDetails("Volume ID: " + getResourceUuid(ApiConstants.ID)); } volume = _volumeService.resizeVolume(this); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/UpdateVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/UpdateVolumeCmd.java index 22b819c8cba2..00c50fa5ffff 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/UpdateVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/UpdateVolumeCmd.java @@ -47,7 +47,7 @@ public class UpdateVolumeCmd extends BaseAsyncCustomIdCmd implements UserCmd { ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) - @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType=VolumeResponse.class, description="the ID of the disk volume") + @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType=VolumeResponse.class, description = "The ID of the disk volume") private Long id; @Parameter(name = ApiConstants.PATH, type = CommandType.STRING, description = "The path of the volume", authorized = {RoleType.Admin}) @@ -71,10 +71,10 @@ public class UpdateVolumeCmd extends BaseAsyncCustomIdCmd implements UserCmd { @Parameter(name = ApiConstants.DISPLAY_VOLUME, type = CommandType.BOOLEAN, - description = "an optional field, whether to the display the volume to the end user or not.", authorized = {RoleType.Admin}) + description = "An optional field, whether to the display the volume to the end User or not.", authorized = {RoleType.Admin}) private Boolean displayVolume; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "new name of the volume", since = "4.16") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "New name of the volume", since = "4.16") private String name; @Parameter(name = ApiConstants.DELETE_PROTECTION, @@ -178,7 +178,7 @@ public String getEventDescription() { @Override public void execute() { - CallContext.current().setEventDetails("Volume Id: " + this._uuidMgr.getUuid(Volume.class, getId())); + CallContext.current().setEventDetails("Volume ID: " + getResourceUuid(ApiConstants.ID)); Volume result = _volumeService.updateVolume(getId(), getPath(), getState(), getStorageId(), getDisplayVolume(), getDeleteProtection(), getCustomId(), getEntityOwnerId(), getChainInfo(), getName()); if (result != null) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/UploadVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/UploadVolumeCmd.java index 339c276d59ea..33a9251e094f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/UploadVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/UploadVolumeCmd.java @@ -32,7 +32,6 @@ import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; -import com.cloud.dc.DataCenter; import com.cloud.event.EventTypes; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; @@ -53,36 +52,36 @@ public class UploadVolumeCmd extends BaseAsyncCmd implements UserCmd { @Parameter(name = ApiConstants.FORMAT, type = CommandType.STRING, required = true, - description = "the format for the volume. Possible values include QCOW2, OVA, and VHD.") + description = "The format for the volume. Possible values include QCOW2, OVA, and VHD.") private String format; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the volume") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "The name of the volume") private String volumeName; @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, length = 2048, - description = "the URL of where the volume is hosted. Possible URL include http:// and https://") + description = "The URL of where the volume is hosted. Possible URL include http:// and https://") private String url; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, - description = "the ID of the zone the volume is to be hosted on") + description = "The ID of the zone the volume is to be hosted on") private Long zoneId; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "an optional domainId. If the account parameter is used, domainId must also be used. If account is NOT provided then volume will be assigned to the caller account and domain.") + description = "An optional domainId. If the Account parameter is used, domainId must also be used. If Account is NOT provided then volume will be assigned to the caller Account and domain.") private Long domainId; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional accountName. Must be used with domainId.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "An optional accountName. Must be used with domainId.") private String accountName; - @Parameter(name = ApiConstants.CHECKSUM, type = CommandType.STRING, description = "the checksum value of this volume. " + ApiConstants.CHECKSUM_PARAMETER_PREFIX_DESCRIPTION) + @Parameter(name = ApiConstants.CHECKSUM, type = CommandType.STRING, description = "The checksum value of this volume. " + ApiConstants.CHECKSUM_PARAMETER_PREFIX_DESCRIPTION) private String checksum; @Parameter(name = ApiConstants.IMAGE_STORE_UUID, type = CommandType.STRING, description = "Image store uuid") @@ -91,7 +90,7 @@ public class UploadVolumeCmd extends BaseAsyncCmd implements UserCmd { @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Upload volume for the project") private Long projectId; - @Parameter(name = ApiConstants.DISK_OFFERING_ID, required = false, type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "the ID of the disk offering. This must be a custom sized offering since during uploadVolume volume size is unknown.") + @Parameter(name = ApiConstants.DISK_OFFERING_ID, required = false, type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "The ID of the disk offering. This must be a custom sized offering since during uploadVolume volume size is unknown.") private Long diskOfferingId; ///////////////////////////////////////////////////// @@ -159,7 +158,7 @@ public String getCommandName() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } @@ -169,7 +168,7 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - return "uploading volume: " + getVolumeName() + " in the zone " + this._uuidMgr.getUuid(DataCenter.class, getZoneId()); + return "Uploading volume: " + getVolumeName() + " to zone with ID: " + getResourceUuid(ApiConstants.ZONE_ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreatePrivateGatewayCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreatePrivateGatewayCmd.java index dceaabf648de..7755abce6f7e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreatePrivateGatewayCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreatePrivateGatewayCmd.java @@ -44,7 +44,7 @@ import com.cloud.network.vpc.Vpc; import com.cloud.network.vpc.VpcGateway; -@APICommand(name = "createPrivateGateway", description = "Creates a private gateway", +@APICommand(name = "createPrivateGateway", description = "Creates a private Gateway", responseObject = PrivateGatewayResponse.class, responseView = ResponseView.Restricted, entityType = {VpcGateway.class}, @@ -58,40 +58,40 @@ public class CreatePrivateGatewayCmd extends BaseAsyncCreateCmd implements UserC //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, required = true, description = "the gateway of the Private gateway") + @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, required = true, description = "The Gateway of the Private Gateway") private String gateway; - @Parameter(name = ApiConstants.NETMASK, type = CommandType.STRING, required = true, description = "the netmask of the Private gateway") + @Parameter(name = ApiConstants.NETMASK, type = CommandType.STRING, required = true, description = "The Netmask of the Private Gateway") private String netmask; - @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, required = true, description = "the IP address of the Private gateaway") + @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, required = true, description = "The IP address of the Private Gateway") private String ipAddress; @Parameter(name = ApiConstants.NETWORK_OFFERING_ID, type = CommandType.UUID, required = false, entityType = NetworkOfferingResponse.class, - description = "the uuid of the network offering to use for the private gateways network connection") + description = "The UUID of the Network offering to use for the private gateways Network connection") private Long networkOfferingId; - @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, required = true, description = "the VPC network belongs to") + @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, required = true, description = "The VPC Network belongs to") private Long vpcId; @Parameter(name = ApiConstants.SOURCE_NAT_SUPPORTED, type = CommandType.BOOLEAN, required = false, - description = "source NAT supported value. Default value false. If 'true' source NAT is enabled on the private gateway" + description = "Source NAT supported value. Default value: false. When 'true', the source NAT is enabled on the private gateway" + " 'false': sourcenat is not supported") private Boolean isSourceNat; - @Parameter(name = ApiConstants.ACL_ID, type = CommandType.UUID, entityType = NetworkACLResponse.class, required = false, description = "the ID of the network ACL") + @Parameter(name = ApiConstants.ACL_ID, type = CommandType.UUID, entityType = NetworkACLResponse.class, required = false, description = "The ID of the Network ACL") private Long aclId; @Parameter(name = ApiConstants.ASSOCIATED_NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, since = "4.17.0", - description = "The isolated network this private gateway is associated to.") + description = "The Isolated Network this private gateway is associated to.") private Long associatedNetworkId; ///////////////////////////////////////////////////// @@ -179,7 +179,7 @@ public void execute() throws InsufficientCapacityException, ConcurrentOperationE public long getEntityOwnerId() { Vpc vpc = _entityMgr.findById(Vpc.class, vpcId); if (vpc == null) { - throw new InvalidParameterValueException("Invalid id is specified for the vpc"); + throw new InvalidParameterValueException("Invalid id is specified for the VPC"); } return vpc.getAccountId(); } @@ -203,7 +203,7 @@ public String getSyncObjType() { public Long getSyncObjId() { Vpc vpc = _entityMgr.findById(Vpc.class, vpcId); if (vpc == null) { - throw new InvalidParameterValueException("Invalid id is specified for the vpc"); + throw new InvalidParameterValueException("Invalid ID is specified for the VPC"); } return vpc.getId(); } 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 c4b5e159b5ce..9b5d25aa0ddb 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 @@ -46,7 +46,7 @@ public class CreateStaticRouteCmd extends BaseAsyncCreateCmd { @Parameter(name = ApiConstants.GATEWAY_ID, type = CommandType.UUID, entityType = PrivateGatewayResponse.class, - description = "the gateway id we are creating static route for. Mutually exclusive with the nexthop parameter") + description = "The gateway ID we are creating static route for. Mutually exclusive with the nexthop parameter") private Long gatewayId; @Parameter(name = ApiConstants.VPC_ID, @@ -62,7 +62,7 @@ public class CreateStaticRouteCmd extends BaseAsyncCreateCmd { since = "4.21.0") private String nextHop; - @Parameter(name = ApiConstants.CIDR, required = true, type = CommandType.STRING, description = "static route cidr") + @Parameter(name = ApiConstants.CIDR, required = true, type = CommandType.STRING, description = "Static route CIDR") private String cidr; ///////////////////////////////////////////////////// @@ -94,7 +94,7 @@ public void create() throws ResourceAllocationException { setEntityId(result.getId()); setEntityUuid(result.getUuid()); } catch (NetworkRuleConflictException ex) { - logger.info("Network rule conflict: " + ex.getMessage()); + logger.info("Network rule conflict: {}", ex.getMessage()); logger.trace("Network rule conflict: ", ex); throw new ServerApiException(ApiErrorCode.NETWORK_RULE_CONFLICT_ERROR, ex.getMessage()); } @@ -115,7 +115,7 @@ public void execute() throws ResourceUnavailableException { boolean success = false; StaticRoute route = null; try { - CallContext.current().setEventDetails("Static route Id: " + getEntityId()); + CallContext.current().setEventDetails("Static route ID: " + getEntityUuid()); success = _vpcService.applyStaticRoute(getEntityId()); // State is different after the route is applied, so retrieve the object only here route = _entityMgr.findById(StaticRoute.class, getEntityId()); @@ -128,7 +128,7 @@ public void execute() throws ResourceUnavailableException { } finally { if (!success || route == null) { _entityMgr.remove(StaticRoute.class, getEntityId()); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create static route"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create a static route"); } } } 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 08b7b9911b1b..86309d7f28a5 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 @@ -52,24 +52,24 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "the account associated with the VPC. " + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "The Account associated with the VPC. " + "Must be used with the domainId parameter.") private String accountName; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "the domain ID associated with the VPC. " + - "If used with the account parameter returns the VPC associated with the account for the specified domain.") + description = "The domain ID associated with the VPC. " + + "If used with the Account parameter returns the VPC associated with the Account for the specified domain.") private Long domainId; @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, - description = "create VPC for the project") + description = "Create VPC for the project") private Long projectId; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, - required = true, description = "the ID of the availability zone") + required = true, description = "The ID of the availability zone") private Long zoneId; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the VPC") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "The name of the VPC") private String vpcName; @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "The display text of the VPC, defaults to its 'name'.") @@ -77,7 +77,7 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd { private String displayText; @Parameter(name = ApiConstants.CIDR, type = CommandType.STRING, - description = "the cidr of the VPC. All VPC guest networks' cidrs should be within this CIDR") + description = "The CIDR of the VPC. All VPC Guest Network's CIDRs should be within this CIDR") private String cidr; @Parameter(name = ApiConstants.CIDR_SIZE, type = CommandType.INTEGER, @@ -86,40 +86,40 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd { private Integer cidrSize; @Parameter(name = ApiConstants.VPC_OFF_ID, type = CommandType.UUID, entityType = VpcOfferingResponse.class, - required = true, description = "the ID of the VPC offering") + required = true, description = "The ID of the VPC offering") private Long vpcOffering; @Parameter(name = ApiConstants.NETWORK_DOMAIN, type = CommandType.STRING, - description = "VPC network domain. All networks inside the VPC will belong to this domain") + description = "VPC Network domain. All Networks inside the VPC will belong to this domain") private String networkDomain; @Parameter(name = ApiConstants.START, type = CommandType.BOOLEAN, - description = "If set to false, the VPC won't start (VPC VR will not get allocated) until its first network gets implemented. " + + description = "If set to false, the VPC won't start (VPC VR will not get allocated) until its first Network gets implemented. " + "True by default.", since = "4.3") private Boolean start; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the vpc to the end user or not", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "An optional field, whether to the display the VPC to the end User or not", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; @Parameter(name = ApiConstants.PUBLIC_MTU, type = CommandType.INTEGER, - description = "MTU to be configured on the network VR's public facing interfaces", since = "4.18.0") + description = "MTU to be configured on the Network VR's public facing interfaces", since = "4.18.0") private Integer publicMtu; - @Parameter(name = ApiConstants.DNS1, type = CommandType.STRING, description = "the first IPv4 DNS for the VPC", since = "4.18.0") + @Parameter(name = ApiConstants.DNS1, type = CommandType.STRING, description = "The first IPv4 DNS for the VPC", since = "4.18.0") private String ip4Dns1; - @Parameter(name = ApiConstants.DNS2, type = CommandType.STRING, description = "the second IPv4 DNS for the VPC", since = "4.18.0") + @Parameter(name = ApiConstants.DNS2, type = CommandType.STRING, description = "The second IPv4 DNS for the VPC", since = "4.18.0") private String ip4Dns2; - @Parameter(name = ApiConstants.IP6_DNS1, type = CommandType.STRING, description = "the first IPv6 DNS for the VPC", since = "4.18.0") + @Parameter(name = ApiConstants.IP6_DNS1, type = CommandType.STRING, description = "The first IPv6 DNS for the VPC", since = "4.18.0") private String ip6Dns1; - @Parameter(name = ApiConstants.IP6_DNS2, type = CommandType.STRING, description = "the second IPv6 DNS for the VPC", since = "4.18.0") + @Parameter(name = ApiConstants.IP6_DNS2, type = CommandType.STRING, description = "The second IPv6 DNS for the VPC", since = "4.18.0") private String ip6Dns2; - @Parameter(name = ApiConstants.SOURCE_NAT_IP, type = CommandType.STRING, description = "IPV4 address to be assigned to the public interface of the network router." + - "This address will be used as source NAT address for the networks in ths VPC. " + - "\nIf an address is given and it cannot be acquired, an error will be returned and the network won´t be implemented,", + @Parameter(name = ApiConstants.SOURCE_NAT_IP, type = CommandType.STRING, description = "IPv4 address to be assigned to the public interface of the Network router." + + "This address will be used as source NAT address for the Networks in ths VPC. " + + "\nIf an address is given and it cannot be acquired, an error will be returned and the Network won´t be implemented,", since = "4.19") private String sourceNatIP; @@ -130,6 +130,11 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd { description="(optional) for NSX based VPCs: when set to true, use the VR IP as nameserver, otherwise use DNS1 and DNS2") private Boolean useVrIpResolver; + @Parameter(name = ApiConstants.KEEP_MAC_ADDRESS_ON_PUBLIC_NIC, + description = ApiConstants.PARAMETER_DESCRIPTION_KEEP_MAC_ADDRESS_ON_PUBLIC_NIC, + type = CommandType.BOOLEAN, since = "4.23.0", authorized = {RoleType.Admin}) + private boolean keepMacAddressOnPublicNic = true; + // /////////////////////////////////////////////////// // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// @@ -214,6 +219,10 @@ public boolean getUseVrIpResolver() { return BooleanUtils.toBoolean(useVrIpResolver); } + public boolean getKeepMacAddressOnPublicNic() { + return keepMacAddressOnPublicNic; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -273,7 +282,7 @@ public String getCommandName() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/DeleteStaticRouteCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/DeleteStaticRouteCmd.java index 01b6aae425b3..cf1805973b77 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/DeleteStaticRouteCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/DeleteStaticRouteCmd.java @@ -44,7 +44,7 @@ public class DeleteStaticRouteCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = StaticRouteResponse.class, required = true, description = "the ID of the static route") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = StaticRouteResponse.class, required = true, description = "The ID of the static route") private Long id; // unexposed parameter needed for events logging @@ -69,7 +69,7 @@ public String getEventType() { @Override public String getEventDescription() { - return ("Deleting static route id=" + id); + return "Deleting static route with ID: " + getResourceUuid(ApiConstants.ID); } @Override @@ -87,7 +87,7 @@ public long getEntityOwnerId() { @Override public void execute() throws ResourceUnavailableException { - CallContext.current().setEventDetails("Route Id: " + id); + CallContext.current().setEventDetails("Route ID: " + getResourceUuid(ApiConstants.ID)); boolean result = _vpcService.revokeStaticRoute(id); if (result) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/DeleteVPCCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/DeleteVPCCmd.java index c35d9084bcc5..e42b4761ea8f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/DeleteVPCCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/DeleteVPCCmd.java @@ -43,7 +43,7 @@ public class DeleteVPCCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VpcResponse.class, required = true, description = "the ID of the VPC") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VpcResponse.class, required = true, description = "The ID of the VPC") private Long id; ///////////////////////////////////////////////////// @@ -65,7 +65,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "Deleting VPC id=" + getId(); + return "Deleting VPC with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListPrivateGatewaysCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListPrivateGatewaysCmd.java index 2304cef3c6d1..aadef68d5ae2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListPrivateGatewaysCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListPrivateGatewaysCmd.java @@ -43,19 +43,19 @@ public class ListPrivateGatewaysCmd extends BaseListProjectAndAccountResourcesCm ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = PrivateGatewayResponse.class, description = "list private gateway by id") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = PrivateGatewayResponse.class, description = "List private gateway by ID") private Long id; - @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, description = "list gateways by ip address") + @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, description = "List gateways by IP address") private String ipAddress; - @Parameter(name = ApiConstants.VLAN, type = CommandType.STRING, description = "list gateways by vlan") + @Parameter(name = ApiConstants.VLAN, type = CommandType.STRING, description = "List gateways by VLAN") private String vlan; - @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, description = "list gateways by vpc") + @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, description = "List gateways by VPC") private Long vpcId; - @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "list gateways by state") + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "List gateways by state") private String state; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListStaticRoutesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListStaticRoutesCmd.java index 36574e2a7777..babc38bc42f1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListStaticRoutesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListStaticRoutesCmd.java @@ -38,16 +38,16 @@ public class ListStaticRoutesCmd extends BaseListTaggedResourcesCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = StaticRouteResponse.class, description = "list static route by id") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = StaticRouteResponse.class, description = "List static route by ID") private Long id; - @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, description = "list static routes by vpc id") + @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, description = "List static routes by VPC ID") private Long vpcId; - @Parameter(name = ApiConstants.GATEWAY_ID, type = CommandType.UUID, entityType = PrivateGatewayResponse.class, description = "list static routes by gateway id") + @Parameter(name = ApiConstants.GATEWAY_ID, type = CommandType.UUID, entityType = PrivateGatewayResponse.class, description = "List static routes by gateway ID") private Long gatewayId; - @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "list static routes by state") + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "List static routes by state") private String state; public Long getId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListVPCOfferingsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListVPCOfferingsCmd.java index f48e113286a9..7d603a85993d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListVPCOfferingsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListVPCOfferingsCmd.java @@ -38,38 +38,38 @@ public class ListVPCOfferingsCmd extends BaseListCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VpcOfferingResponse.class, description = "list VPC offerings by id") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VpcOfferingResponse.class, description = "List VPC offerings by ID") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "list VPC offerings by name") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "List VPC offerings by name") private String vpcOffName; - @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "list VPC offerings by display text") + @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "List VPC offerings by display text") private String displayText; - @Parameter(name = ApiConstants.IS_DEFAULT, type = CommandType.BOOLEAN, description = "true if need to list only default " + "VPC offerings. Default value is false") + @Parameter(name = ApiConstants.IS_DEFAULT, type = CommandType.BOOLEAN, description = "True if need to list only default " + "VPC offerings. Default value is false") private Boolean isDefault; @Parameter(name = ApiConstants.SUPPORTED_SERVICES, type = CommandType.LIST, collectionType = CommandType.STRING, - description = "list VPC offerings supporting certain services") + description = "List VPC offerings supporting certain services") private List supportedServices; - @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "list VPC offerings by state") + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "List VPC offerings by state") private String state; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "list VPC offerings available for VPC creation in specific domain", + description = "List VPC offerings available for VPC creation in specific domain", since = "4.18") private Long domainId; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, - description = "id of zone VPC offering is associated with", + description = "ID of zone VPC offering is associated with", since = "4.13") private Long zoneId; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListVPCsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListVPCsCmd.java index d128be1414d2..c4597d9b986c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListVPCsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListVPCsCmd.java @@ -46,39 +46,39 @@ public class ListVPCsCmd extends BaseListTaggedResourcesCmd implements UserCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// //////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VpcResponse.class, description = "list VPC by id") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VpcResponse.class, description = "List VPC by ID") private Long id; - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "list by zone") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "List by zone") private Long zoneId; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "list by name of the VPC") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "List by name of the VPC") private String vpcName; - @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "List by display text of " + "the VPC") + @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "List by display text of " + "The VPC") private String displayText; - @Parameter(name = ApiConstants.CIDR, type = CommandType.STRING, description = "list by cidr of the VPC. All VPC " - + "guest networks' cidrs should be within this CIDR") + @Parameter(name = ApiConstants.CIDR, type = CommandType.STRING, description = "List by CIDR of the VPC. All VPC " + + "Guest Network's CIDRs should be within this CIDR") private String cidr; - @Parameter(name = ApiConstants.VPC_OFF_ID, type = CommandType.UUID, entityType = VpcOfferingResponse.class, description = "list by ID of the VPC offering") + @Parameter(name = ApiConstants.VPC_OFF_ID, type = CommandType.UUID, entityType = VpcOfferingResponse.class, description = "List by ID of the VPC offering") private Long VpcOffId; - @Parameter(name = ApiConstants.SUPPORTED_SERVICES, type = CommandType.LIST, collectionType = CommandType.STRING, description = "list VPC supporting certain services") + @Parameter(name = ApiConstants.SUPPORTED_SERVICES, type = CommandType.LIST, collectionType = CommandType.STRING, description = "List VPC supporting certain services") private List supportedServices; - @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "list VPCs by state") + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "List VPCs by state") private String state; - @Parameter(name = ApiConstants.RESTART_REQUIRED, type = CommandType.BOOLEAN, description = "list VPCs by restartRequired option") + @Parameter(name = ApiConstants.RESTART_REQUIRED, type = CommandType.BOOLEAN, description = "List VPCs by restartRequired option") private Boolean restartRequired; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, - description = "flag to display the resource icon for VPCs") + description = "Flag to display the resource icon for VPCs") private Boolean showIcon; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/RestartVPCCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/RestartVPCCmd.java index 5ccd496eeb4c..f6a408a66a3d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/RestartVPCCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/RestartVPCCmd.java @@ -43,10 +43,10 @@ public class RestartVPCCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VpcResponse.class, required = true, description = "the id of the VPC") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VpcResponse.class, required = true, description = "The ID of the VPC") private Long id; - @Parameter(name = ApiConstants.CLEANUP, type = CommandType.BOOLEAN, required = false, description = "If cleanup old network elements") + @Parameter(name = ApiConstants.CLEANUP, type = CommandType.BOOLEAN, required = false, description = "Should we cleanup the old Network elements") private Boolean cleanup = false; @Parameter(name = ApiConstants.MAKEREDUNDANT, type = CommandType.BOOLEAN, required = false, description = "Turn a single VPC into a redundant one.") @@ -118,7 +118,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "restarting VPC id=" + getId(); + return "Restarting VPC with ID: " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmd.java index 6fcfb5311f62..6cfdb895977d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmd.java @@ -47,28 +47,33 @@ public class UpdateVPCCmd extends BaseAsyncCustomIdCmd implements UserCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VpcResponse.class, required = true, description = "the id of the VPC") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VpcResponse.class, required = true, description = "The ID of the VPC") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the VPC") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the VPC") private String vpcName; - @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "the display text of the VPC") + @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "The display text of the VPC") private String displayText; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the vpc to the end user or not", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "Optional field, should we display the VPC to the end User or not", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; @Parameter(name = ApiConstants.PUBLIC_MTU, type = CommandType.INTEGER, - description = "MTU to be configured on the network VR's public facing interfaces", since = "4.18.0") + description = "MTU to be configured on the Network VR's public facing interfaces", since = "4.18.0") private Integer publicMtu; @Parameter(name = ApiConstants.SOURCE_NAT_IP, type = CommandType.STRING, - description = "IPV4 address to be assigned to the public interface of the network router. This address must already be acquired for this VPC", + description = "IPV4 address to be assigned to the public interface of the Network router. This address must already be acquired for this VPC", since = "4.19") private String sourceNatIP; + @Parameter(name = ApiConstants.KEEP_MAC_ADDRESS_ON_PUBLIC_NIC, + description = ApiConstants.PARAMETER_DESCRIPTION_KEEP_MAC_ADDRESS_ON_PUBLIC_NIC, + type = CommandType.BOOLEAN, since = "4.23.0", authorized = {RoleType.Admin}) + private Boolean keepMacAddressOnPublicNic; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -97,6 +102,10 @@ public String getSourceNatIP() { return sourceNatIP; } + public Boolean getKeepMacAddressOnPublicNic() { + return keepMacAddressOnPublicNic; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -143,7 +152,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "updating VPC id=" + getId(); + return "Updating VPC " + getResourceUuid(ApiConstants.ID); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/AddVpnUserCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/AddVpnUserCmd.java index 59ba7e94b04d..78cd9a3ac7e4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/AddVpnUserCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/AddVpnUserCmd.java @@ -28,12 +28,11 @@ import org.apache.cloudstack.api.response.VpnUsersResponse; import org.apache.cloudstack.context.CallContext; -import com.cloud.domain.Domain; import com.cloud.event.EventTypes; import com.cloud.network.VpnUser; import com.cloud.user.Account; -@APICommand(name = "addVpnUser", description = "Adds vpn users", responseObject = VpnUsersResponse.class, entityType = {VpnUser.class}, +@APICommand(name = "addVpnUser", description = "Adds VPN Users", responseObject = VpnUsersResponse.class, entityType = {VpnUser.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class AddVpnUserCmd extends BaseAsyncCreateCmd { @@ -41,22 +40,22 @@ public class AddVpnUserCmd extends BaseAsyncCreateCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, required = true, description = "username for the vpn user") + @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, required = true, description = "Username for the VPN User") private String userName; - @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, required = true, description = "password for the username") + @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, required = true, description = "Password for the username") private String password; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the vpn user. Must be used with domainId.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "An optional Account for the VPN User. Must be used with domainId.") private String accountName; - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "add vpn user to the specific project") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Add VPN User to the specific project") private Long projectId; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "an optional domainId for the vpn user. If the account parameter is used, domainId must also be used.") + description = "An optional domainId for the VPN User. If the account parameter is used, domainId must also be used.") private Long domainId; ///////////////////////////////////////////////////// @@ -89,7 +88,7 @@ public Long getProjectId() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } @@ -99,7 +98,7 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - return "Add Remote Access VPN user for account " + getEntityOwnerId() + " username= " + getUserName(); + return "Add Remote Access VPN User for Account " + getEntityOwnerId() + " username= " + getUserName(); } @Override @@ -110,32 +109,17 @@ public String getEventType() { @Override public void execute() { VpnUser vpnUser = _entityMgr.findById(VpnUser.class, getEntityId()); - Account account = _entityMgr.findById(Account.class, vpnUser.getAccountId()); try { if (!_ravService.applyVpnUsers(vpnUser.getAccountId(), userName)) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add vpn user"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add VPN User"); } } catch (Exception ex) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); } - - VpnUsersResponse vpnResponse = new VpnUsersResponse(); - vpnResponse.setId(vpnUser.getUuid()); - vpnResponse.setUserName(vpnUser.getUsername()); - vpnResponse.setAccountName(account.getAccountName()); // re-retrieve the vpnuser, as the call to `applyVpnUsers` might have changed the state vpnUser = _entityMgr.findById(VpnUser.class, getEntityId()); - vpnResponse.setState(vpnUser.getState().toString()); - - Domain domain = _entityMgr.findById(Domain.class, account.getDomainId()); - if (domain != null) { - vpnResponse.setDomainId(domain.getUuid()); - vpnResponse.setDomainName(domain.getName()); - vpnResponse.setDomainPath(domain.getPath()); - } - + VpnUsersResponse vpnResponse = _responseGenerator.createVpnUserResponse(vpnUser); vpnResponse.setResponseName(getCommandName()); - vpnResponse.setObjectName("vpnuser"); setResponseObject(vpnResponse); } @@ -145,7 +129,7 @@ public void create() { VpnUser vpnUser = _ravService.addVpnUser(owner.getId(), userName, password); if (vpnUser == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add vpn user"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add VPN User"); } setEntityId(vpnUser.getId()); setEntityUuid(vpnUser.getUuid()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateRemoteAccessVpnCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateRemoteAccessVpnCmd.java index 417ba2773c41..c730c2c5fe88 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateRemoteAccessVpnCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateRemoteAccessVpnCmd.java @@ -37,7 +37,7 @@ import com.cloud.network.IpAddress; import com.cloud.network.RemoteAccessVpn; -@APICommand(name = "createRemoteAccessVpn", description = "Creates a l2tp/ipsec remote access vpn", responseObject = RemoteAccessVpnResponse.class, entityType = {RemoteAccessVpn.class}, +@APICommand(name = "createRemoteAccessVpn", description = "Creates a l2tp/ipsec remote access VPN", responseObject = RemoteAccessVpnResponse.class, entityType = {RemoteAccessVpn.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateRemoteAccessVpnCmd extends BaseAsyncCreateCmd { @@ -49,32 +49,32 @@ public class CreateRemoteAccessVpnCmd extends BaseAsyncCreateCmd { type = CommandType.UUID, entityType = IPAddressResponse.class, required = true, - description = "public ip address id of the vpn server") + description = "Public IP address id of the VPN server") private Long publicIpId; @Parameter(name = "iprange", type = CommandType.STRING, required = false, - description = "the range of ip addresses to allocate to vpn clients. The first ip in the range will be taken by the vpn server") + description = "The range of IP addresses to allocate to VPN clients. The first IP in the range will be taken by the VPN server") private String ipRange; @Deprecated - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the VPN. Must be used with domainId.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "An optional Account for the VPN. Must be used with domainId.") private String accountName; @Deprecated @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "an optional domainId for the VPN. If the account parameter is used, domainId must also be used.") + description = "An optional domainId for the VPN. If the account parameter is used, domainId must also be used.") private Long domainId; @Parameter(name = ApiConstants.OPEN_FIREWALL, type = CommandType.BOOLEAN, - description = "if true, firewall rule for source/end public port is automatically created; if false - firewall rule has to be created explicitly. Has value true by default") + description = "If true, firewall rule for source/end public port is automatically created; if false - firewall rule has to be created explicitly. Has value true by default") private Boolean openFirewall; - @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}) + @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; ///////////////////////////////////////////////////// @@ -127,7 +127,7 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - return "Create Remote Access VPN for account " + getEntityOwnerId() + " using public ip id=" + publicIpId; + return "Create Remote Access VPN for Account " + getEntityOwnerId() + " using public ip id=" + publicIpId; } @Override @@ -143,10 +143,10 @@ public void create() { setEntityId(vpn.getId()); setEntityUuid(vpn.getUuid()); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create remote access vpn"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create remote access VPN"); } } catch (NetworkRuleConflictException e) { - logger.info("Network rule conflict: " + e.getMessage()); + logger.info("Network rule conflict: {}", e.getMessage()); logger.trace("Network Rule Conflict: ", e); throw new ServerApiException(ApiErrorCode.NETWORK_RULE_CONFLICT_ERROR, e.getMessage()); } @@ -161,7 +161,7 @@ public void execute() { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create remote access vpn"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create remote access VPN"); } } catch (ResourceUnavailableException ex) { logger.warn("Exception: ", ex); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnConnectionCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnConnectionCmd.java index 0b5c46d36eb9..3d6b7918effd 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnConnectionCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnConnectionCmd.java @@ -37,7 +37,7 @@ import com.cloud.network.vpc.Vpc; -@APICommand(name = "createVpnConnection", description = "Create site to site vpn connection", responseObject = Site2SiteVpnConnectionResponse.class, entityType = {Site2SiteVpnConnection.class}, +@APICommand(name = "createVpnConnection", description = "Create site to site VPN connection", responseObject = Site2SiteVpnConnectionResponse.class, entityType = {Site2SiteVpnConnection.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateVpnConnectionCmd extends BaseAsyncCreateCmd { @@ -49,20 +49,20 @@ public class CreateVpnConnectionCmd extends BaseAsyncCreateCmd { type = CommandType.UUID, entityType = Site2SiteVpnGatewayResponse.class, required = true, - description = "id of the vpn gateway") + description = "ID of the VPN gateway") private Long vpnGatewayId; @Parameter(name = ApiConstants.S2S_CUSTOMER_GATEWAY_ID, type = CommandType.UUID, entityType = Site2SiteCustomerGatewayResponse.class, required = true, - description = "id of the customer gateway") + description = "ID of the customer gateway") private Long customerGatewayId; - @Parameter(name = ApiConstants.PASSIVE, type = CommandType.BOOLEAN, required = false, description = "connection is passive or not") + @Parameter(name = ApiConstants.PASSIVE, type = CommandType.BOOLEAN, required = false, description = "Connection is passive or not") private Boolean passive; - @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}) + @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; ///////////////////////////////////////////////////// @@ -114,7 +114,7 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - return "Create site-to-site VPN connection for account " + getEntityOwnerId(); + return "Create site-to-site VPN connection for Account " + getEntityOwnerId(); } @Override @@ -130,10 +130,10 @@ public void create() { setEntityId(conn.getId()); setEntityUuid(conn.getUuid()); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create site to site vpn connection"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create site to site VPN connection"); } } catch (NetworkRuleConflictException e) { - logger.info("Network rule conflict: " + e.getMessage()); + logger.info("Network rule conflict: {}", e.getMessage()); logger.trace("Network Rule Conflict: ", e); throw new ServerApiException(ApiErrorCode.NETWORK_RULE_CONFLICT_ERROR, e.getMessage()); } @@ -148,7 +148,7 @@ public void execute() { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create site to site vpn connection"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create site to site VPN connection"); } } catch (ResourceUnavailableException ex) { logger.warn("Exception: ", ex); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnCustomerGatewayCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnCustomerGatewayCmd.java index a2fa0d9829c8..0da813eb4867 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnCustomerGatewayCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnCustomerGatewayCmd.java @@ -33,7 +33,7 @@ import com.cloud.exception.ResourceAllocationException; import com.cloud.network.Site2SiteCustomerGateway; -@APICommand(name = "createVpnCustomerGateway", description = "Creates site to site vpn customer gateway", responseObject = Site2SiteCustomerGatewayResponse.class, entityType = {Site2SiteCustomerGateway.class}, +@APICommand(name = "createVpnCustomerGateway", description = "Creates site to site VPN customer gateway", responseObject = Site2SiteCustomerGatewayResponse.class, entityType = {Site2SiteCustomerGateway.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateVpnCustomerGatewayCmd extends BaseAsyncCreateCmd { @@ -41,13 +41,13 @@ public class CreateVpnCustomerGatewayCmd extends BaseAsyncCreateCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = false, description = "name of this customer gateway") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = false, description = "Name of this customer gateway") private String name; - @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, required = true, description = "public ip address id of the customer gateway") + @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, required = true, description = "Public IP address id of the customer gateway") private String gatewayIp; - @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.STRING, required = true, description = "guest cidr list of the customer gateway. Multiple entries must be separated by a single comma character (,).") + @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.STRING, required = true, description = "Guest cidr list of the customer gateway. Multiple entries must be separated by a single comma character (,).") private String peerCidrList; @Parameter(name = ApiConstants.IPSEC_PSK, type = CommandType.STRING, required = true, description = "IPsec Preshared-Key of the customer gateway. Cannot contain newline or double quotes.") @@ -77,18 +77,18 @@ public class CreateVpnCustomerGatewayCmd extends BaseAsyncCreateCmd { @Parameter(name = ApiConstants.FORCE_ENCAP, type = CommandType.BOOLEAN, required = false, description = "Force Encapsulation for NAT traversal") private Boolean encap; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "the account associated with the gateway. Must be used with the domainId parameter.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "The Account associated with the gateway. Must be used with the domainId parameter.") private String accountName; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "the domain ID associated with the gateway. If used with the account parameter returns the " - + "gateway associated with the account for the specified domain.") + description = "The domain ID associated with the gateway. If used with the account parameter returns the " + + "gateway associated with the Account for the specified domain.") private Long domainId; @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, - description = "create site-to-site VPN customer gateway for the project", since = "4.6") + description = "Create site-to-site VPN customer gateway for the project", since = "4.6") private Long projectId; @Parameter(name = ApiConstants.SPLIT_CONNECTIONS, type = CommandType.BOOLEAN, required = false, description = "For IKEv2, whether to split multiple right subnet cidrs into multiple connection statements.", @@ -167,7 +167,7 @@ public String getIkeVersion() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { accountId = CallContext.current().getCallingAccount().getId(); } @@ -176,7 +176,7 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - return "Create site-to-site VPN customer gateway for account " + getEntityOwnerId(); + return "Create site-to-site VPN customer gateway for Account " + getEntityOwnerId(); } @Override 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 6c3f57b3dfa8..665f699c4ef3 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 @@ -33,7 +33,7 @@ import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.context.CallContext; -@APICommand(name = "createVpnGateway", description = "Creates site to site vpn local gateway", responseObject = Site2SiteVpnGatewayResponse.class, entityType = {Site2SiteVpnGateway.class}, +@APICommand(name = "createVpnGateway", description = "Creates site to site VPN local gateway", responseObject = Site2SiteVpnGatewayResponse.class, entityType = {Site2SiteVpnGateway.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateVpnGatewayCmd extends BaseAsyncCreateCmd { @@ -45,7 +45,7 @@ public class CreateVpnGatewayCmd extends BaseAsyncCreateCmd { type = CommandType.UUID, entityType = VpcResponse.class, required = true, - description = "id of the vpc") + description = "Public IP address id of the VPN gateway") private Long vpcId; @Parameter(name = ApiConstants.IP_ADDRESS_ID, @@ -55,7 +55,7 @@ public class CreateVpnGatewayCmd extends BaseAsyncCreateCmd { 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}) + @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; ///////////////////////////////////////////////////// @@ -96,7 +96,7 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - return "Create site-to-site VPN gateway for account " + getEntityOwnerId(); + return "Create site-to-site VPN gateway for Account " + getEntityOwnerId(); } @Override @@ -106,7 +106,7 @@ public String getEventType() { @Override public void execute() { - CallContext.current().setEventDetails("VPN gateway Id: " + getEntityId()); + CallContext.current().setEventDetails("VPN gateway ID: " + getEntityUuid()); Site2SiteVpnGateway result = _s2sVpnService.getVpnGateway(getEntityId()); if (result != null) { Site2SiteVpnGatewayResponse response = _responseGenerator.createSite2SiteVpnGatewayResponse(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/DeleteRemoteAccessVpnCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/DeleteRemoteAccessVpnCmd.java index bf8d01579238..b1fc331f4c3a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/DeleteRemoteAccessVpnCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/DeleteRemoteAccessVpnCmd.java @@ -34,7 +34,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.RemoteAccessVpn; -@APICommand(name = "deleteRemoteAccessVpn", description = "Destroys a l2tp/ipsec remote access vpn", responseObject = SuccessResponse.class, entityType = {RemoteAccessVpn.class}, +@APICommand(name = "deleteRemoteAccessVpn", description = "Destroys a l2tp/ipsec remote access VPN", responseObject = SuccessResponse.class, entityType = {RemoteAccessVpn.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DeleteRemoteAccessVpnCmd extends BaseAsyncCmd { @@ -47,7 +47,7 @@ public class DeleteRemoteAccessVpnCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = IPAddressResponse.class, required = true, - description = "public ip address id of the vpn server") + description = "Public IP address id of the VPN server") private Long publicIpId; // unexposed parameter needed for events logging @@ -69,14 +69,14 @@ public long getEntityOwnerId() { if (vpnEntity != null) return vpnEntity.getAccountId(); - throw new InvalidParameterValueException("The specified public ip is not allocated to any account"); + throw new InvalidParameterValueException("The specified public ip is not allocated to any Account"); } return ownerId; } @Override public String getEventDescription() { - return "Delete Remote Access VPN for account " + getEntityOwnerId() + " for ip id=" + publicIpId; + return "Delete Remote Access VPN for Account " + getEntityOwnerId() + " for IP: " + getResourceUuid(ApiConstants.PUBLIC_IP_ID); } @Override @@ -87,7 +87,7 @@ public String getEventType() { @Override public void execute() throws ResourceUnavailableException { if (! _ravService.destroyRemoteAccessVpnForIp(publicIpId, CallContext.current().getCallingAccount(), false)) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete remote access vpn"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete remote access VPN"); } SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/DeleteVpnConnectionCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/DeleteVpnConnectionCmd.java index 2528d93a0422..b23e6c163020 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/DeleteVpnConnectionCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/DeleteVpnConnectionCmd.java @@ -31,7 +31,7 @@ import com.cloud.network.Site2SiteVpnConnection; import com.cloud.user.Account; -@APICommand(name = "deleteVpnConnection", description = "Delete site to site vpn connection", responseObject = SuccessResponse.class, entityType = {Site2SiteVpnConnection.class}, +@APICommand(name = "deleteVpnConnection", description = "Delete site to site VPN connection", responseObject = SuccessResponse.class, entityType = {Site2SiteVpnConnection.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DeleteVpnConnectionCmd extends BaseAsyncCmd { @@ -39,7 +39,7 @@ public class DeleteVpnConnectionCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = Site2SiteVpnConnectionResponse.class, required = true, description = "id of vpn connection") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = Site2SiteVpnConnectionResponse.class, required = true, description = "ID of VPN connection") private Long id; ///////////////////////////////////////////////////// @@ -65,7 +65,7 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - return "Delete site-to-site VPN connection for account " + getEntityOwnerId(); + return "Deleting site-to-site VPN connection for Account " + getEntityOwnerId(); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/DeleteVpnCustomerGatewayCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/DeleteVpnCustomerGatewayCmd.java index 2b657fd3c088..9057620e0ddb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/DeleteVpnCustomerGatewayCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/DeleteVpnCustomerGatewayCmd.java @@ -33,7 +33,7 @@ import com.cloud.network.Site2SiteCustomerGateway; import com.cloud.user.Account; -@APICommand(name = "deleteVpnCustomerGateway", description = "Delete site to site vpn customer gateway", responseObject = SuccessResponse.class, entityType = {Site2SiteCustomerGateway.class}, +@APICommand(name = "deleteVpnCustomerGateway", description = "Delete site to site VPN customer gateway", responseObject = SuccessResponse.class, entityType = {Site2SiteCustomerGateway.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DeleteVpnCustomerGatewayCmd extends BaseAsyncCmd { @@ -46,7 +46,7 @@ public class DeleteVpnCustomerGatewayCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = Site2SiteCustomerGatewayResponse.class, required = true, - description = "id of customer gateway") + description = "ID of customer gateway") private Long id; ///////////////////////////////////////////////////// @@ -72,7 +72,7 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - return "Delete site-to-site VPN customer gateway for account " + getEntityOwnerId(); + return "Deleting site-to-site VPN customer gateway for Account " + getEntityOwnerId(); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/DeleteVpnGatewayCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/DeleteVpnGatewayCmd.java index 27ded12dc58d..bfea59a3e6f3 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/DeleteVpnGatewayCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/DeleteVpnGatewayCmd.java @@ -30,7 +30,7 @@ import com.cloud.network.Site2SiteVpnGateway; import com.cloud.user.Account; -@APICommand(name = "deleteVpnGateway", description = "Delete site to site vpn gateway", responseObject = SuccessResponse.class, entityType = {Site2SiteVpnGateway.class}, +@APICommand(name = "deleteVpnGateway", description = "Delete site to site VPN gateway", responseObject = SuccessResponse.class, entityType = {Site2SiteVpnGateway.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DeleteVpnGatewayCmd extends BaseAsyncCmd { @@ -38,7 +38,7 @@ public class DeleteVpnGatewayCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = Site2SiteVpnGatewayResponse.class, required = true, description = "id of customer gateway") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = Site2SiteVpnGatewayResponse.class, required = true, description = "ID of customer gateway") private Long id; ///////////////////////////////////////////////////// @@ -64,7 +64,7 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - return "Delete site-to-site VPN gateway for account " + getEntityOwnerId(); + return "Delete site-to-site VPN gateway for Account " + getEntityOwnerId(); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ListRemoteAccessVpnsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ListRemoteAccessVpnsCmd.java index 4efc70c84199..185d11bc29d4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ListRemoteAccessVpnsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ListRemoteAccessVpnsCmd.java @@ -33,7 +33,7 @@ import com.cloud.network.RemoteAccessVpn; import com.cloud.utils.Pair; -@APICommand(name = "listRemoteAccessVpns", description = "Lists remote access vpns", responseObject = RemoteAccessVpnResponse.class, entityType = {RemoteAccessVpn.class}, +@APICommand(name = "listRemoteAccessVpns", description = "Lists remote access VPNs", responseObject = RemoteAccessVpnResponse.class, entityType = {RemoteAccessVpn.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListRemoteAccessVpnsCmd extends BaseListProjectAndAccountResourcesCmd { @@ -41,24 +41,24 @@ public class ListRemoteAccessVpnsCmd extends BaseListProjectAndAccountResourcesC ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.PUBLIC_IP_ID, type = CommandType.UUID, entityType = IPAddressResponse.class, description = "public ip address id of the vpn server") + @Parameter(name = ApiConstants.PUBLIC_IP_ID, type = CommandType.UUID, entityType = IPAddressResponse.class, description = "Public ip address id of the VPN server") private Long publicIpId; @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = RemoteAccessVpnResponse.class, - description = "Lists remote access vpn rule with the specified ID", + description = "Lists remote access VPN rule with the specified ID", since = "4.3") private Long id; @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, - description = "list remote access VPNs for certain network", + description = "List remote access VPNs for certain Network", since = "4.3") private Long networkId; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ListVpnConnectionsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ListVpnConnectionsCmd.java index aeeae44d0046..9a37e2f786c0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ListVpnConnectionsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ListVpnConnectionsCmd.java @@ -32,7 +32,7 @@ import com.cloud.network.Site2SiteVpnConnection; import com.cloud.utils.Pair; -@APICommand(name = "listVpnConnections", description = "Lists site to site vpn connection gateways", responseObject = Site2SiteVpnConnectionResponse.class, entityType = {Site2SiteVpnConnection.class}, +@APICommand(name = "listVpnConnections", description = "Lists site to site VPN connection gateways", responseObject = Site2SiteVpnConnectionResponse.class, entityType = {Site2SiteVpnConnection.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListVpnConnectionsCmd extends BaseListProjectAndAccountResourcesCmd { @@ -40,13 +40,13 @@ public class ListVpnConnectionsCmd extends BaseListProjectAndAccountResourcesCmd ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = Site2SiteVpnConnectionResponse.class, description = "id of the vpn connection") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = Site2SiteVpnConnectionResponse.class, description = "ID of the VPN connection") private Long id; - @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, description = "id of vpc") + @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, description = "ID of VPC") private Long vpcId; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ListVpnCustomerGatewaysCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ListVpnCustomerGatewaysCmd.java index 258a8a753ebe..51e761839718 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ListVpnCustomerGatewaysCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ListVpnCustomerGatewaysCmd.java @@ -30,7 +30,7 @@ import com.cloud.network.Site2SiteCustomerGateway; import com.cloud.utils.Pair; -@APICommand(name = "listVpnCustomerGateways", description = "Lists site to site vpn customer gateways", responseObject = Site2SiteCustomerGatewayResponse.class, entityType = {Site2SiteCustomerGateway.class}, +@APICommand(name = "listVpnCustomerGateways", description = "Lists site to site VPN customer gateways", responseObject = Site2SiteCustomerGatewayResponse.class, entityType = {Site2SiteCustomerGateway.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListVpnCustomerGatewaysCmd extends BaseListProjectAndAccountResourcesCmd { @@ -39,7 +39,7 @@ public class ListVpnCustomerGatewaysCmd extends BaseListProjectAndAccountResourc //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = Site2SiteCustomerGatewayResponse.class, description = "id of the customer gateway") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = Site2SiteCustomerGatewayResponse.class, description = "ID of the customer gateway") private Long id; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ListVpnGatewaysCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ListVpnGatewaysCmd.java index d30fbf8d32bb..3bdcf79677a0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ListVpnGatewaysCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ListVpnGatewaysCmd.java @@ -32,7 +32,7 @@ import com.cloud.network.Site2SiteVpnGateway; import com.cloud.utils.Pair; -@APICommand(name = "listVpnGateways", description = "Lists site 2 site vpn gateways", responseObject = Site2SiteVpnGatewayResponse.class, entityType = {Site2SiteVpnGateway.class}, +@APICommand(name = "listVpnGateways", description = "Lists site 2 site VPN gateways", responseObject = Site2SiteVpnGatewayResponse.class, entityType = {Site2SiteVpnGateway.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListVpnGatewaysCmd extends BaseListProjectAndAccountResourcesCmd { @@ -41,13 +41,13 @@ public class ListVpnGatewaysCmd extends BaseListProjectAndAccountResourcesCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = Site2SiteVpnGatewayResponse.class, description = "id of the vpn gateway") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = Site2SiteVpnGatewayResponse.class, description = "ID of the VPN gateway") private Long id; - @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, description = "id of vpc") + @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, description = "ID of VPC ") private Long vpcId; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ListVpnUsersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ListVpnUsersCmd.java index 48591765ec34..b4a621e53f91 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ListVpnUsersCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ListVpnUsersCmd.java @@ -30,7 +30,7 @@ import com.cloud.network.VpnUser; import com.cloud.utils.Pair; -@APICommand(name = "listVpnUsers", description = "Lists vpn users", responseObject = VpnUsersResponse.class, entityType = {VpnUser.class}, +@APICommand(name = "listVpnUsers", description = "Lists VPN Users", responseObject = VpnUsersResponse.class, entityType = {VpnUser.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListVpnUsersCmd extends BaseListProjectAndAccountResourcesCmd { @@ -38,10 +38,10 @@ public class ListVpnUsersCmd extends BaseListProjectAndAccountResourcesCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VpnUsersResponse.class, description = "The uuid of the Vpn user") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VpnUsersResponse.class, description = "The UUID of the VPN User") private Long id; - @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, description = "the username of the vpn user.") + @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, description = "The username of the VPN User.") private String userName; ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/RemoveVpnUserCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/RemoveVpnUserCmd.java index 0697987b04de..a18619c89498 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/RemoveVpnUserCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/RemoveVpnUserCmd.java @@ -33,7 +33,7 @@ import com.cloud.network.VpnUser; import com.cloud.user.Account; -@APICommand(name = "removeVpnUser", description = "Removes vpn user", responseObject = SuccessResponse.class, entityType = {VpnUser.class}, +@APICommand(name = "removeVpnUser", description = "Removes VPN User", responseObject = SuccessResponse.class, entityType = {VpnUser.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class RemoveVpnUserCmd extends BaseAsyncCmd { @@ -41,19 +41,19 @@ public class RemoveVpnUserCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, required = true, description = "username for the vpn user") + @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, required = true, description = "Username for the VPN User") private String userName; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the vpn user. Must be used with domainId.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "An optional Account for the VPN User. Must be used with domainId.") private String accountName; - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "remove vpn user from the project") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Remove VPN User from the project") private Long projectId; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "an optional domainId for the vpn user. If the account parameter is used, domainId must also be used.") + description = "An optional domainId for the VPN User. If the Account parameter is used, domainId must also be used.") private Long domainId; ///////////////////////////////////////////////////// @@ -82,7 +82,7 @@ public Long getProjecId() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } @@ -92,7 +92,7 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - return "Remove Remote Access VPN user for account " + getEntityOwnerId() + " username= " + getUserName(); + return "Remove Remote Access VPN User for Account " + getEntityOwnerId() + " username= " + getUserName(); } @Override @@ -106,7 +106,7 @@ public void execute() { long ownerId = owner.getId(); boolean result = _ravService.removeVpnUser(owner, userName, CallContext.current().getCallingAccount()); if (!result) { - String errorMessage = String.format("Failed to remove VPN user=[%s]. VPN owner id=[%s].", userName, ownerId); + String errorMessage = String.format("Failed to remove VPN User=[%s]. VPN owner id=[%s].", userName, ownerId); logger.error(errorMessage); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, errorMessage); } @@ -115,13 +115,13 @@ public void execute() { try { appliedVpnUsers = _ravService.applyVpnUsers(ownerId, userName, true); } catch (ResourceUnavailableException ex) { - String errorMessage = String.format("Failed to refresh VPN user=[%s] due to resource unavailable. VPN owner id=[%s].", userName, ownerId); + String errorMessage = String.format("Failed to refresh VPN User=[%s] due to resource unavailable. VPN owner id=[%s].", userName, ownerId); logger.error(errorMessage, ex); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, errorMessage, ex); } if (!appliedVpnUsers) { - String errorMessage = String.format("Failed to refresh VPN user=[%s]. VPN owner id=[%s].", userName, ownerId); + String errorMessage = String.format("Failed to refresh VPN User=[%s]. VPN owner id=[%s].", userName, ownerId); logger.debug(errorMessage); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, errorMessage); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ResetVpnConnectionCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ResetVpnConnectionCmd.java index 736295b4119c..f681c8cce182 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ResetVpnConnectionCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/ResetVpnConnectionCmd.java @@ -32,7 +32,7 @@ import com.cloud.network.Site2SiteVpnConnection; import com.cloud.user.Account; -@APICommand(name = "resetVpnConnection", description = "Reset site to site vpn connection", responseObject = Site2SiteVpnConnectionResponse.class, entityType = {Site2SiteVpnConnection.class}, +@APICommand(name = "resetVpnConnection", description = "Reset site to site VPN connection", responseObject = Site2SiteVpnConnectionResponse.class, entityType = {Site2SiteVpnConnection.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ResetVpnConnectionCmd extends BaseAsyncCmd { @@ -40,16 +40,16 @@ public class ResetVpnConnectionCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = Site2SiteVpnConnectionResponse.class, required = true, description = "id of vpn connection") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = Site2SiteVpnConnectionResponse.class, required = true, description = "ID of VPN connection") private Long id; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for connection. " + "Must be used with domainId.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "An optional Account for connection. " + "Must be used with domainId.") private String accountName; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "an optional domainId for connection. If the account parameter is used, domainId must also be used.") + description = "An optional domainId for connection. If the Account parameter is used, domainId must also be used.") private Long domainId; ///////////////////////////////////////////////////// @@ -74,7 +74,7 @@ public Long getId() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, null, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, null, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } @@ -83,7 +83,7 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - return "Reset site-to-site VPN connection for account " + getEntityOwnerId(); + return "Reset site-to-site VPN connection for Account " + getEntityOwnerId(); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateRemoteAccessVpnCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateRemoteAccessVpnCmd.java index defde70b63ac..edf2b9e630bc 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateRemoteAccessVpnCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateRemoteAccessVpnCmd.java @@ -28,7 +28,7 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.network.RemoteAccessVpn; -@APICommand(name = "updateRemoteAccessVpn", description = "Updates remote access vpn", responseObject = RemoteAccessVpnResponse.class, since = "4.4", +@APICommand(name = "updateRemoteAccessVpn", description = "Updates remote access VPN", responseObject = RemoteAccessVpnResponse.class, since = "4.4", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class UpdateRemoteAccessVpnCmd extends BaseAsyncCustomIdCmd { @@ -37,14 +37,14 @@ public class UpdateRemoteAccessVpnCmd extends BaseAsyncCustomIdCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, required = true, entityType = RemoteAccessVpnResponse.class, description = "id of the remote access vpn") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, required = true, entityType = RemoteAccessVpnResponse.class, description = "ID of the remote access VPN") private Long id; // unexposed parameter needed for events logging @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, expose = false) private Long ownerId; - @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}) + @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; ///////////////////////////////////////////////////// @@ -73,7 +73,7 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - return "Updating remote access vpn id=" + id; + return "Updating remote access VPN id=" + id; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateVpnConnectionCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateVpnConnectionCmd.java index 62dd6167b753..92f6786268ad 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateVpnConnectionCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateVpnConnectionCmd.java @@ -27,7 +27,7 @@ import com.cloud.network.Site2SiteVpnConnection; import com.cloud.user.Account; -@APICommand(name = "updateVpnConnection", description = "Updates site to site vpn connection", responseObject = Site2SiteVpnConnectionResponse.class, since = "4.4", +@APICommand(name = "updateVpnConnection", description = "Updates site to site VPN connection", responseObject = Site2SiteVpnConnectionResponse.class, since = "4.4", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class UpdateVpnConnectionCmd extends BaseAsyncCustomIdCmd { @@ -35,10 +35,10 @@ public class UpdateVpnConnectionCmd extends BaseAsyncCustomIdCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = Site2SiteVpnConnectionResponse.class, required = true, description = "id of vpn connection") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = Site2SiteVpnConnectionResponse.class, required = true, description = "ID of VPN connection") private Long id; - @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}) + @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; ///////////////////////////////////////////////////// @@ -66,7 +66,7 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - return "Updating site-to-site VPN connection id= " + id; + return "Updating site-to-site VPN connection ID = " + id; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateVpnCustomerGatewayCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateVpnCustomerGatewayCmd.java index 9f3ac2ec4367..56aa8b2cd16d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateVpnCustomerGatewayCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateVpnCustomerGatewayCmd.java @@ -32,7 +32,7 @@ import com.cloud.event.EventTypes; import com.cloud.network.Site2SiteCustomerGateway; -@APICommand(name = "updateVpnCustomerGateway", description = "Update site to site vpn customer gateway", responseObject = Site2SiteCustomerGatewayResponse.class, entityType = {Site2SiteCustomerGateway.class}, +@APICommand(name = "updateVpnCustomerGateway", description = "Update site to site VPN customer gateway", responseObject = Site2SiteCustomerGatewayResponse.class, entityType = {Site2SiteCustomerGateway.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class UpdateVpnCustomerGatewayCmd extends BaseAsyncCmd { @@ -44,16 +44,16 @@ public class UpdateVpnCustomerGatewayCmd extends BaseAsyncCmd { type = CommandType.UUID, entityType = Site2SiteCustomerGatewayResponse.class, required = true, - description = "id of customer gateway") + description = "ID of customer gateway") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = false, description = "name of this customer gateway") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = false, description = "Name of this customer gateway") private String name; - @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, required = true, description = "public ip address id of the customer gateway") + @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, required = true, description = "Public IP address id of the customer gateway") private String gatewayIp; - @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.STRING, required = true, description = "guest cidr of the customer gateway. Multiple entries must be separated by a single comma character (,).") + @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.STRING, required = true, description = "Guest CIDR of the customer gateway. Multiple entries must be separated by a single comma character (,).") private String guestCidrList; @Parameter(name = ApiConstants.IPSEC_PSK, type = CommandType.STRING, required = true, description = "IPsec Preshared-Key of the customer gateway. Cannot contain newline or double quotes.") @@ -83,14 +83,14 @@ public class UpdateVpnCustomerGatewayCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.FORCE_ENCAP, type = CommandType.BOOLEAN, required = false, description = "Force encapsulation for Nat Traversal") private Boolean encap; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "the account associated with the gateway. Must be used with the domainId parameter.") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "The Account associated with the gateway. Must be used with the domainId parameter.") private String accountName; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, - description = "the domain ID associated with the gateway. If used with the account parameter returns the " - + "gateway associated with the account for the specified domain.") + description = "The domain ID associated with the gateway. If used with the account parameter returns the " + + "gateway associated with the Account for the specified domain.") private Long domainId; @Parameter(name = ApiConstants.SPLIT_CONNECTIONS, type = CommandType.BOOLEAN, required = false, description = "For IKEv2, whether to split multiple right subnet cidrs into multiple connection statements.", @@ -161,7 +161,7 @@ public String getIkeVersion() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, null, true); + Long accountId = _accountService.finalizeAccountId(accountName, domainId, null, true); if (accountId == null) { accountId = CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateVpnGatewayCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateVpnGatewayCmd.java index 9fe5ae0480f7..25076991217f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateVpnGatewayCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateVpnGatewayCmd.java @@ -27,7 +27,7 @@ import com.cloud.network.Site2SiteVpnGateway; import com.cloud.user.Account; -@APICommand(name = "updateVpnGateway", description = "Updates site to site vpn local gateway", responseObject = Site2SiteVpnGatewayResponse.class, since = "4.4", +@APICommand(name = "updateVpnGateway", description = "Updates site to site VPN local gateway", responseObject = Site2SiteVpnGatewayResponse.class, since = "4.4", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class UpdateVpnGatewayCmd extends BaseAsyncCustomIdCmd { @@ -35,10 +35,10 @@ public class UpdateVpnGatewayCmd extends BaseAsyncCustomIdCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = Site2SiteVpnGatewayResponse.class, required = true, description = "id of customer gateway") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = Site2SiteVpnGatewayResponse.class, required = true, description = "ID of customer gateway") private Long id; - @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}) + @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; ///////////////////////////////////////////////////// @@ -63,7 +63,7 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - return "Update site-to-site VPN gateway id= " + id; + return "Update site-to-site VPN gateway ID = " + id; } @Override 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 36ce23cbc6ee..55f70b9edcdf 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 @@ -37,7 +37,7 @@ public class ListZonesCmd extends BaseListCmd implements UserCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the ID of the zone") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "The ID of the zone") private Long id; @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = ZoneResponse.class, description = "the IDs of the zones, mutually exclusive with id", since = "4.19.0") @@ -45,26 +45,26 @@ public class ListZonesCmd extends BaseListCmd implements UserCmd { @Parameter(name = ApiConstants.AVAILABLE, type = CommandType.BOOLEAN, - description = "true if you want to retrieve all available Zones. False if you only want to return the Zones" - + " from which you have at least one VM. Default is false.") + description = "True if you want to retrieve all available Zones. False if you only want to return the Zones" + + " from which you have at least one Instance. Default is false.") private Boolean available; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "the ID of the domain associated with the zone") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "The ID of the domain associated with the zone") private Long domainId; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the zone") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the zone") private String name; - @Parameter(name = ApiConstants.NETWORK_TYPE, type = CommandType.STRING, description = "the network type of the zone that the virtual machine belongs to") + @Parameter(name = ApiConstants.NETWORK_TYPE, type = CommandType.STRING, description = "The Network type of the zone that the Instance belongs to") private String networkType; - @Parameter(name = ApiConstants.SHOW_CAPACITIES, type = CommandType.BOOLEAN, description = "flag to display the capacity of the zones") + @Parameter(name = ApiConstants.SHOW_CAPACITIES, type = CommandType.BOOLEAN, description = "Flag to display the capacity of the zones") private Boolean showCapacities; @Parameter(name = ApiConstants.TAGS, type = CommandType.MAP, description = "List zones by resource tags (key/value pairs)", since = "4.3") private Map tags; - @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, description = "flag to display the resource image for the zones") + @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, 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 000000000000..433a37c07cde --- /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 3c99e2cbec62..93529b8a8887 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 @@ -32,99 +32,99 @@ @EntityReference(value = Account.class) public class AccountResponse extends BaseResponse implements ResourceLimitAndCountResponse, SetResourceIconResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the id of the account") + @Param(description = "The ID of the Account") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the account") + @Param(description = "The name of the Account") private String name; @SerializedName(ApiConstants.ACCOUNT_TYPE) - @Param(description = "account type (admin, domain-admin, user)") + @Param(description = "Account type (admin, domain-admin, user)") private Integer accountType; @SerializedName(ApiConstants.ROLE_ID) - @Param(description = "the ID of the role") + @Param(description = "The ID of the role") private String roleId; @SerializedName(ApiConstants.ROLE_TYPE) - @Param(description = "the type of the role (Admin, ResourceAdmin, DomainAdmin, User)") + @Param(description = "The type of the role (Admin, ResourceAdmin, DomainAdmin, User)") private String roleType; @SerializedName(ApiConstants.ROLE_NAME) - @Param(description = "the name of the role") + @Param(description = "The name of the role") private String roleName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "id of the Domain the account belongs to") + @Param(description = "ID of the Domain the Account belongs to") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "name of the Domain the account belongs to") + @Param(description = "Name of the Domain the Account belongs to") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) - @Param(description = "path of the Domain the account belongs to", since = "4.13") + @Param(description = "Path of the Domain the Account belongs to", since = "4.13") private String domainPath; @SerializedName(ApiConstants.DEFAULT_ZONE_ID) - @Param(description = "the default zone of the account") + @Param(description = "The default zone of the Account") private String defaultZoneId; @SerializedName(ApiConstants.RECEIVED_BYTES) - @Param(description = "the total number of network traffic bytes received") + @Param(description = "The total number of Network traffic bytes received") private Long bytesReceived; @SerializedName(ApiConstants.SENT_BYTES) - @Param(description = "the total number of network traffic bytes sent") + @Param(description = "The total number of Network traffic bytes sent") private Long bytesSent; @SerializedName(ApiConstants.VM_LIMIT) - @Param(description = "the total number of virtual machines that can be deployed by this account") + @Param(description = "The total number of Instances that can be deployed by this Account") private String vmLimit; @SerializedName(ApiConstants.VM_TOTAL) - @Param(description = "the total number of virtual machines deployed by this account") + @Param(description = "The total number of Instances deployed by this Account") private Long vmTotal; @SerializedName(ApiConstants.VM_AVAILABLE) - @Param(description = "the total number of virtual machines available for this account to acquire") + @Param(description = "The total number of Instances available for this Account to acquire") private String vmAvailable; @SerializedName(ApiConstants.IP_LIMIT) - @Param(description = "the total number of public ip addresses this account can acquire") + @Param(description = "The total number of public IP addresses this Account can acquire") private String ipLimit; @SerializedName(ApiConstants.IP_TOTAL) - @Param(description = "the total number of public ip addresses allocated for this account") + @Param(description = "The total number of public IP addresses allocated for this Account") private Long ipTotal; @SerializedName(ApiConstants.IP_AVAILABLE) - @Param(description = "the total number of public ip addresses available for this account to acquire") + @Param(description = "The total number of public IP addresses available for this Account to acquire") private String ipAvailable; @SerializedName("volumelimit") - @Param(description = "the total volume which can be used by this account") + @Param(description = "The total volume which can be used by this Account") private String volumeLimit; @SerializedName("volumetotal") - @Param(description = "the total volume being used by this account") + @Param(description = "The total volume being used by this Account") private Long volumeTotal; @SerializedName("volumeavailable") - @Param(description = "the total volume available for this account") + @Param(description = "The total volume available for this Account") private String volumeAvailable; @SerializedName("snapshotlimit") - @Param(description = "the total number of snapshots which can be stored by this account") + @Param(description = "The total number of Snapshots which can be stored by this Account") private String snapshotLimit; @SerializedName("snapshottotal") - @Param(description = "the total number of snapshots stored by this account") + @Param(description = "The total number of Snapshots stored by this Account") private Long snapshotTotal; @SerializedName("snapshotavailable") - @Param(description = "the total number of snapshots available for this account") + @Param(description = "The total number of Snapshots available for this Account") private String snapshotAvailable; @SerializedName(ApiConstants.BACKUP_LIMIT) @@ -152,83 +152,83 @@ public class AccountResponse extends BaseResponse implements ResourceLimitAndCou private String backupStorageAvailable; @SerializedName("templatelimit") - @Param(description = "the total number of templates which can be created by this account") + @Param(description = "The total number of Templates which can be created by this Account") private String templateLimit; @SerializedName("templatetotal") - @Param(description = "the total number of templates which have been created by this account") + @Param(description = "The total number of Templates which have been created by this Account") private Long templateTotal; @SerializedName("templateavailable") - @Param(description = "the total number of templates available to be created by this account") + @Param(description = "The total number of Templates available to be created by this Account") private String templateAvailable; @SerializedName("vmstopped") - @Param(description = "the total number of virtual machines stopped for this account") + @Param(description = "The total number of Instances stopped for this Account") private Integer vmStopped; @SerializedName("vmrunning") - @Param(description = "the total number of virtual machines running for this account") + @Param(description = "The total number of Instances running for this Account") private Integer vmRunning; @SerializedName("projectlimit") - @Param(description = "the total number of projects the account can own", since = "3.0.1") + @Param(description = "The total number of projects the Account can own", since = "3.0.1") private String projectLimit; @SerializedName("projecttotal") - @Param(description = "the total number of projects being administrated by this account", since = "3.0.1") + @Param(description = "The total number of projects being administrated by this Account", since = "3.0.1") private Long projectTotal; @SerializedName("projectavailable") - @Param(description = "the total number of projects available for administration by this account", since = "3.0.1") + @Param(description = "The total number of projects available for administration by this Account", since = "3.0.1") private String projectAvailable; @SerializedName("networklimit") - @Param(description = "the total number of networks the account can own", since = "3.0.1") + @Param(description = "The total number of Networks the Account can own", since = "3.0.1") private String networkLimit; @SerializedName("networktotal") - @Param(description = "the total number of networks owned by account", since = "3.0.1") + @Param(description = "The total number of Networks owned by Account", since = "3.0.1") private Long networkTotal; @SerializedName("networkavailable") - @Param(description = "the total number of networks available to be created for this account", since = "3.0.1") + @Param(description = "The total number of Networks available to be created for this Account", since = "3.0.1") private String networkAvailable; @SerializedName("vpclimit") - @Param(description = "the total number of vpcs the account can own", since = "4.0.0") + @Param(description = "The total number of VPCs the Account can own", since = "4.0.0") private String vpcLimit; @SerializedName("vpctotal") - @Param(description = "the total number of vpcs owned by account", since = "4.0.0") + @Param(description = "The total number of VPCs owned by account", since = "4.0.0") private Long vpcTotal; @SerializedName("vpcavailable") - @Param(description = "the total number of vpcs available to be created for this account", since = "4.0.0") + @Param(description = "The total number of VPCs available to be created for this account", since = "4.0.0") private String vpcAvailable; @SerializedName("cpulimit") - @Param(description = "the total number of cpu cores the account can own", since = "4.2.0") + @Param(description = "The total number of CPU cores the account can own", since = "4.2.0") private String cpuLimit; @SerializedName("cputotal") - @Param(description = "the total number of cpu cores owned by account", since = "4.2.0") + @Param(description = "The total number of CPU cores owned by account", since = "4.2.0") private Long cpuTotal; @SerializedName("cpuavailable") - @Param(description = "the total number of cpu cores available to be created for this account", since = "4.2.0") + @Param(description = "The total number of CPU cores available to be created for this account", since = "4.2.0") private String cpuAvailable; @SerializedName("memorylimit") - @Param(description = "the total memory (in MB) the account can own", since = "4.2.0") + @Param(description = "The total memory (in MB) the account can own", since = "4.2.0") private String memoryLimit; @SerializedName("memorytotal") - @Param(description = "the total memory (in MB) owned by account", since = "4.2.0") + @Param(description = "The total memory (in MB) owned by account", since = "4.2.0") private Long memoryTotal; @SerializedName("memoryavailable") - @Param(description = "the total memory (in MB) available to be created for this account", since = "4.2.0") + @Param(description = "The total memory (in MB) available to be created for this account", since = "4.2.0") private String memoryAvailable; @SerializedName("gpulimit") @@ -244,27 +244,27 @@ public class AccountResponse extends BaseResponse implements ResourceLimitAndCou private String gpuAvailable; @SerializedName("primarystoragelimit") - @Param(description = "the total primary storage space (in GiB) the account can own", since = "4.2.0") + @Param(description = "The total primary storage space (in GiB) the account can own", since = "4.2.0") private String primaryStorageLimit; @SerializedName("primarystoragetotal") - @Param(description = "the total primary storage space (in GiB) owned by account", since = "4.2.0") + @Param(description = "The total primary storage space (in GiB) owned by account", since = "4.2.0") private Long primaryStorageTotal; @SerializedName("primarystorageavailable") - @Param(description = "the total primary storage space (in GiB) available to be used for this account", since = "4.2.0") + @Param(description = "The total primary storage space (in GiB) available to be used for this account", since = "4.2.0") private String primaryStorageAvailable; @SerializedName("secondarystoragelimit") - @Param(description = "the total secondary storage space (in GiB) the account can own", since = "4.2.0") + @Param(description = "The total secondary storage space (in GiB) the account can own", since = "4.2.0") private String secondaryStorageLimit; @SerializedName("secondarystoragetotal") - @Param(description = "the total secondary storage space (in GiB) owned by account", since = "4.2.0") + @Param(description = "The total secondary storage space (in GiB) owned by account", since = "4.2.0") private float secondaryStorageTotal; @SerializedName("secondarystorageavailable") - @Param(description = "the total secondary storage space (in GiB) available to be used for this account", since = "4.2.0") + @Param(description = "The total secondary storage space (in GiB) available to be used for this account", since = "4.2.0") private String secondaryStorageAvailable; @SerializedName(ApiConstants.BUCKET_LIMIT) @@ -292,35 +292,35 @@ public class AccountResponse extends BaseResponse implements ResourceLimitAndCou private String objectStorageAvailable; @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the account") + @Param(description = "The state of the account") private String state; @SerializedName(ApiConstants.IS_CLEANUP_REQUIRED) - @Param(description = "true if the account requires cleanup") + @Param(description = "True if the account requires cleanup") private Boolean cleanupRequired; @SerializedName(ApiConstants.CREATED) - @Param(description="the date when this account was created") + @Param(description = "The date when this account was created") private Date created; @SerializedName("user") - @Param(description = "the list of users associated with account", responseObject = UserResponse.class) + @Param(description = "The list of users associated with account", responseObject = UserResponse.class) private List users; @SerializedName(ApiConstants.NETWORK_DOMAIN) - @Param(description = "the network domain") + @Param(description = "The Network domain") private String networkDomain; @SerializedName(ApiConstants.ACCOUNT_DETAILS) - @Param(description = "details for the account") + @Param(description = "Details for the account") private Map details; @SerializedName(ApiConstants.IS_DEFAULT) - @Param(description = "true if account is default, false otherwise", since = "4.2.0") + @Param(description = "True if account is default, false otherwise", since = "4.2.0") private Boolean isDefault; @SerializedName(ApiConstants.IAM_GROUPS) - @Param(description = "the list of acl groups that account belongs to", since = "4.4") + @Param(description = "The list of ACL groups that account belongs to", since = "4.4") private List groups; @SerializedName(ApiConstants.RESOURCE_ICON) @@ -569,7 +569,7 @@ public void setNetworkAvailable(String networkAvailable) { @Override public void setVpcLimit(String vpcLimit) { - this.vpcLimit = networkLimit; + this.vpcLimit = vpcLimit; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/response/AcquireIPAddressResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/AcquireIPAddressResponse.java index 06351a40d9db..cb0b14054775 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/AcquireIPAddressResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/AcquireIPAddressResponse.java @@ -32,47 +32,47 @@ @SuppressWarnings("unused") public class AcquireIPAddressResponse extends BaseResponse implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "public IP address id") + @Param(description = "Public IP address id") private String id; @SerializedName(ApiConstants.IP_ADDRESS) - @Param(description = "public IP address") + @Param(description = "Public IP address") private String ipAddress; @SerializedName("allocated") - @Param(description = "date the public IP address was acquired") + @Param(description = "Date the public IP address was acquired") private Date allocated; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the ID of the zone the public IP address belongs to") + @Param(description = "The ID of the zone the public IP address belongs to") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "the name of the zone the public IP address belongs to") + @Param(description = "The name of the zone the public IP address belongs to") private String zoneName; @SerializedName("issourcenat") - @Param(description = "true if the IP address is a source nat address, false otherwise") + @Param(description = "True if the IP address is a source NAT address, false otherwise") private Boolean sourceNat; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account the public IP address is associated with") + @Param(description = "The Account the public IP address is associated with") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the ipaddress") + @Param(description = "The project id of the IP address") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the address") + @Param(description = "The project name of the address") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID the public IP address is associated with") + @Param(description = "The domain ID the public IP address is associated with") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain the public IP address is associated with") + @Param(description = "The domain the public IP address is associated with") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) @@ -80,78 +80,78 @@ public class AcquireIPAddressResponse extends BaseResponse implements Controlle private String domainPath; @SerializedName(ApiConstants.FOR_VIRTUAL_NETWORK) - @Param(description = "the virtual network for the IP address") + @Param(description = "The virtual Network for the IP address") private Boolean forVirtualNetwork; @SerializedName(ApiConstants.VLAN_ID) - @Param(description = "the ID of the VLAN associated with the IP address." + " This parameter is visible to ROOT admins only") + @Param(description = "The ID of the VLAN associated with the IP address." + " This parameter is visible to ROOT admins only") private String vlanId; @SerializedName("vlanname") - @Param(description = "the VLAN associated with the IP address") + @Param(description = "The VLAN associated with the IP address") private String vlanName; @SerializedName("isstaticnat") - @Param(description = "true if this ip is for static nat, false otherwise") + @Param(description = "True if this IP is for static NAT, false otherwise") private Boolean staticNat; @SerializedName(ApiConstants.IS_SYSTEM) - @Param(description = "true if this ip is system ip (was allocated as a part of deployVm or createLbRule)") + @Param(description = "True if this IP is system IP (was allocated as a part of deployVm or createLbRule)") private Boolean isSystem; @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) - @Param(description = "virtual machine id the ip address is assigned to (not null only for static nat Ip)") + @Param(description = "Instance id the IP address is assigned to (not null only for static NAT IP)") private String virtualMachineId; @SerializedName("vmipaddress") - @Param(description = "virtual machine (dnat) ip address (not null only for static nat Ip)") + @Param(description = "Instance (DNAT) IP address (not null only for static NAT IP)") private String virtualMachineIp; @SerializedName("virtualmachinename") - @Param(description = "virtual machine name the ip address is assigned to (not null only for static nat Ip)") + @Param(description = "Instance name the IP address is assigned to (not null only for static NAT IP)") private String virtualMachineName; @SerializedName("virtualmachinedisplayname") - @Param(description = "virtual machine display name the ip address is assigned to (not null only for static nat Ip)") + @Param(description = "Instance display name the IP address is assigned to (not null only for static NAT IP)") private String virtualMachineDisplayName; @SerializedName(ApiConstants.ASSOCIATED_NETWORK_ID) - @Param(description = "the ID of the Network associated with the IP address") + @Param(description = "The ID of the Network associated with the IP address") private String associatedNetworkId; @SerializedName(ApiConstants.ASSOCIATED_NETWORK_NAME) - @Param(description = "the name of the Network associated with the IP address") + @Param(description = "The name of the Network associated with the IP address") private String associatedNetworkName; @SerializedName(ApiConstants.NETWORK_ID) - @Param(description = "the ID of the Network where ip belongs to") + @Param(description = "The ID of the Network where IP belongs to") private String networkId; @SerializedName(ApiConstants.STATE) - @Param(description = "State of the ip address. Can be: Allocating, Allocated and Releasing") + @Param(description = "State of the IP address. Can be: Allocating, Allocated and Releasing") private String state; @SerializedName(ApiConstants.PHYSICAL_NETWORK_ID) - @Param(description = "the physical network this belongs to") + @Param(description = "The physical Network this belongs to") private String physicalNetworkId; @SerializedName(ApiConstants.PURPOSE) - @Param(description = "purpose of the IP address. In Acton this value is not null for Ips with isSystem=true, and can have either StaticNat or LB value") + @Param(description = "Purpose of the IP address. In Acton this value is not null for IPs with isSystem=true, and can have either StaticNat or LB value") private String purpose; @SerializedName(ApiConstants.VPC_ID) - @Param(description = "VPC the ip belongs to") + @Param(description = "VPC the IP belongs to") private String vpcId; @SerializedName(ApiConstants.TAGS) - @Param(description = "the list of resource tags associated with ip address", responseObject = ResourceTagResponse.class) + @Param(description = "The list of resource tags associated with IP address", responseObject = ResourceTagResponse.class) private List tags; @SerializedName(ApiConstants.IS_PORTABLE) - @Param(description = "is public IP portable across the zones") + @Param(description = "Is public IP portable across the zones") private Boolean isPortable; @SerializedName(ApiConstants.FOR_DISPLAY) - @Param(description = "is public ip for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) + @Param(description = "Is public IP for display to the regular User", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; public void setIpAddress(String ipAddress) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/AcquirePodIpCmdResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/AcquirePodIpCmdResponse.java index 2226efd063d2..fb95c5b8130c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/AcquirePodIpCmdResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/AcquirePodIpCmdResponse.java @@ -31,7 +31,7 @@ public class AcquirePodIpCmdResponse extends BaseResponse { private String ipAddress; @SerializedName(ApiConstants.POD_ID) - @Param(description = "the ID of the pod the IP address belongs to") + @Param(description = "The ID of the pod the IP address belongs to") private Long podId; @SerializedName(ApiConstants.GATEWAY) @@ -43,15 +43,15 @@ public class AcquirePodIpCmdResponse extends BaseResponse { private String cidrAddress; @SerializedName(ApiConstants.NIC_ID) - @Param(description = "the ID of the nic") + @Param(description = "The ID of the NIC") private Long nicId; @SerializedName(ApiConstants.HOST_MAC) - @Param(description = "MAC address of the pod the IP") + @Param(description = "MAC address of the pod the IP") private Long macAddress; @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the pod the IP address") + @Param(description = "The ID of the pod the IP address") private long id; public void setIpAddress(String ipAddress) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/AlertResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/AlertResponse.java index faa64b0c0909..00c94a6fd0e7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/AlertResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/AlertResponse.java @@ -31,7 +31,7 @@ @SuppressWarnings("unused") public class AlertResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the id of the alert") + @Param(description = "The ID of the alert") private String id; @SerializedName(ApiConstants.TYPE) @@ -46,15 +46,15 @@ public class AlertResponse extends BaseResponse { private Short alertType; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the alert", since = "4.3") + @Param(description = "The name of the alert", since = "4.3") private String alertName; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "description of the alert") + @Param(description = "Description of the alert") private String description; @SerializedName(ApiConstants.SENT) - @Param(description = "the date and time the alert was sent") + @Param(description = "The date and time the alert was sent") private Date lastSent; public void setId(String id) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/AlertTypeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/AlertTypeResponse.java index 3f91cde01788..e8c3cf6c4ac7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/AlertTypeResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/AlertTypeResponse.java @@ -16,11 +16,12 @@ // 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 com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + public class AlertTypeResponse extends BaseResponse { @SerializedName("alerttypeid") @@ -31,6 +32,10 @@ public class AlertTypeResponse extends BaseResponse { @Param(description = "description of alert type") private String name; + @SerializedName(ApiConstants.REPETITION_ALLOWED) + @Param(description = "Whether repetitive alerts allowed for the alert type", since = "4.22.0") + private boolean repetitionAllowed = true; + public String getName() { return name; } @@ -47,9 +52,10 @@ public void setUsageType(short alertType) { this.alertType = alertType; } - public AlertTypeResponse(short alertType, String name) { + public AlertTypeResponse(short alertType, String name, boolean repetitionAllowed) { this.alertType = alertType; this.name = name; + this.repetitionAllowed = repetitionAllowed; setObjectName("alerttype"); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/AnnotationResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/AnnotationResponse.java index 86d485ac751e..62ffd6c6256d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/AnnotationResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/AnnotationResponse.java @@ -32,31 +32,31 @@ @EntityReference(value = Annotation.class) public class AnnotationResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the (uu)id of the annotation") + @Param(description = "The UUID of the annotation") private String uuid; @SerializedName(ApiConstants.ENTITY_TYPE) - @Param(description = "the type of the annotated entity") + @Param(description = "The type of the annotated entity") private String entityType; @SerializedName(ApiConstants.ENTITY_ID) - @Param(description = "the (uu)id of the entity to which this annotation pertains") + @Param(description = "The UUID of the entity to which this annotation pertains") private String entityUuid; @SerializedName(ApiConstants.ENTITY_NAME) - @Param(description = "the name of the entity to which this annotation pertains") + @Param(description = "The name of the entity to which this annotation pertains") private String entityName; @SerializedName(ApiConstants.ANNOTATION) - @Param(description = "the contents of the annotation") + @Param(description = "The contents of the annotation") private String annotation; @SerializedName(ApiConstants.USER_ID) - @Param(description = "The (uu)id of the user that entered the annotation") + @Param(description = "The UUID of the User that entered the annotation") private String userUuid; @SerializedName(ApiConstants.USERNAME) - @Param(description = "The username of the user that entered the annotation") + @Param(description = "The username of the User that entered the annotation") private String username; @SerializedName(ApiConstants.ADMINS_ONLY) @@ -64,11 +64,11 @@ public class AnnotationResponse extends BaseResponse { private Boolean adminsOnly; @SerializedName(ApiConstants.CREATED) - @Param(description = "the creation timestamp for this annotation") + @Param(description = "The creation timestamp for this annotation") private Date created; @SerializedName(ApiConstants.REMOVED) - @Param(description = "the removal timestamp for this annotation") + @Param(description = "The removal timestamp for this annotation") private Date removed; public String getUuid() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ApiKeyPairResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ApiKeyPairResponse.java new file mode 100644 index 000000000000..350d71b37887 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ApiKeyPairResponse.java @@ -0,0 +1,285 @@ +// 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.user.ApiKeyPairState; +import com.google.gson.annotations.SerializedName; + +import java.util.Date; +import java.util.List; + +import org.apache.cloudstack.acl.apikeypair.ApiKeyPair; +import org.apache.cloudstack.api.ApiConstants; + +import com.cloud.serializer.Param; +import org.apache.cloudstack.api.BaseResponseWithAnnotations; +import org.apache.cloudstack.api.EntityReference; + +@EntityReference(value = ApiKeyPair.class) +public class ApiKeyPairResponse extends BaseResponseWithAnnotations { + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the API key pair") + private String name; + + @SerializedName(ApiConstants.API_KEY) + @Param(description = "The API key of the registered user.", isSensitive = true) + private String userApiKey; + + @SerializedName(ApiConstants.SECRET_KEY) + @Param(description = "The secret key of the registered user.", isSensitive = true) + private String userSecretKey; + + @SerializedName(ApiConstants.USER_ID) + @Param(description = "ID of the user that owns the keypair.") + private String userId; + + @SerializedName(ApiConstants.USERNAME) + @Param(description = "Username of the keypair's owner.") + private String username; + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the API key pair.", isSensitive = true) + private String id; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "API key pair description.") + private String description; + + @SerializedName(ApiConstants.START_DATE) + @Param(description = "API key pair start date.") + private Date startDate; + + @SerializedName(ApiConstants.END_DATE) + @Param(description = "API key pair expiration date.") + private Date endDate; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "API key pair creation timestamp.") + private Date created; + + @SerializedName(ApiConstants.ACCOUNT_TYPE) + @Param(description = "Account type.") + private String accountType; + + @SerializedName(ApiConstants.ACCOUNT_ID) + @Param(description = "Account ID.") + private String accountId; + + @SerializedName(ApiConstants.ACCOUNT_NAME) + @Param(description = "Account name.") + private String accountName; + + @SerializedName(ApiConstants.ROLE_ID) + @Param(description = "ID of the role.") + private String roleId; + + @SerializedName(ApiConstants.ROLE_TYPE) + @Param(description = "Type of the role (Admin, ResourceAdmin, DomainAdmin, User).") + private String roleType; + + @SerializedName(ApiConstants.ROLE_NAME) + @Param(description = "Name of the role.") + private String roleName; + + @SerializedName(ApiConstants.PERMISSIONS) + @Param(description = "Permissions of the API key pair.") + private List permissions; + + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "ID of the domain which the account belongs to.") + private String domainId; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "Name of the domain which the account belongs to.") + private String domainName; + + @SerializedName(ApiConstants.DOMAIN_PATH) + @Param(description = "Path of the domain which the account belongs to.") + private String domainPath; + + @SerializedName(ApiConstants.STATE) + @Param(description = "State of the API key pair.") + private ApiKeyPairState state; + + public String getApiKey() { + return userApiKey; + } + + public void setApiKey(String apiKey) { + this.userApiKey = apiKey; + } + + public String getSecretKey() { + return userSecretKey; + } + + public void setSecretKey(String secretKey) { + this.userSecretKey = secretKey; + } + + public String getId() { + return this.id; + } + + public void setId(String id) { + this.id = id; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Date getStartDate() { + return startDate; + } + + public void setStartDate(Date startDate) { + this.startDate = startDate; + } + + public Date getEndDate() { + return endDate; + } + + public void setEndDate(Date endDate) { + this.endDate = endDate; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAccountType() { + return accountType; + } + + public void setAccountType(String accountType) { + this.accountType = accountType; + } + + public String getRoleId() { + return roleId; + } + + public void setRoleId(String roleId) { + this.roleId = roleId; + } + + public String getAccountId() { + return accountId; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getRoleType() { + return roleType; + } + + public void setRoleType(String roleType) { + this.roleType = roleType; + } + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + public String getDomainId() { + return domainId; + } + + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + public String getDomainName() { + return domainName; + } + + public void setDomainName(String domainName) { + this.domainName = domainName; + } + + public String getDomainPath() { + return domainPath; + } + + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + + public ApiKeyPairState getState() { + return state; + } + + public void setState(ApiKeyPairState state) { + this.state = state; + } + + public String getAccountName() { + return accountName; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public List getPermissions() { + return permissions; + } + + public void setPermissions(List permissions) { + this.permissions = permissions; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ApplicationLoadBalancerInstanceResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ApplicationLoadBalancerInstanceResponse.java index bdbb954df03a..2ebc25e72404 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ApplicationLoadBalancerInstanceResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ApplicationLoadBalancerInstanceResponse.java @@ -32,19 +32,19 @@ public class ApplicationLoadBalancerInstanceResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the instance ID") + @Param(description = "The Instance ID") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the instance") + @Param(description = "The name of the Instance") private String name; @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the instance") + @Param(description = "The state of the Instance") private String state; @SerializedName(ApiConstants.IP_ADDRESS) - @Param(description = "the ip address of the instance") + @Param(description = "The IP address of the Instance") private String ipAddress; public void setId(String id) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ApplicationLoadBalancerResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ApplicationLoadBalancerResponse.java index 0b6485bfc142..e900bebe05f0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ApplicationLoadBalancerResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ApplicationLoadBalancerResponse.java @@ -29,51 +29,51 @@ @SuppressWarnings("unused") public class ApplicationLoadBalancerResponse extends BaseResponse implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the Load Balancer ID") + @Param(description = "The Load Balancer ID") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the Load Balancer") + @Param(description = "The name of the Load Balancer") private String name; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "the description of the Load Balancer") + @Param(description = "The description of the Load Balancer") private String description; @SerializedName(ApiConstants.ALGORITHM) - @Param(description = "the load balancer algorithm (source, roundrobin, leastconn)") + @Param(description = "The Load balancer algorithm (source, roundrobin, leastconn)") private String algorithm; @SerializedName(ApiConstants.NETWORK_ID) - @Param(description = "Load Balancer network id") + @Param(description = "Load Balancer Network id") private String networkId; @SerializedName(ApiConstants.SOURCE_IP) - @Param(description = "Load Balancer source ip") + @Param(description = "Load Balancer source IP") private String sourceIp; @SerializedName(ApiConstants.SOURCE_IP_NETWORK_ID) - @Param(description = "Load Balancer source ip network id") + @Param(description = "Load Balancer source IP Network id") private String sourceIpNetworkId; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account of the Load Balancer") + @Param(description = "The Account of the Load Balancer") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the Load Balancer") + @Param(description = "The project id of the Load Balancer") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the Load Balancer") + @Param(description = "The project name of the Load Balancer") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID of the Load Balancer") + @Param(description = "The domain ID of the Load Balancer") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain of the Load Balancer") + @Param(description = "The domain of the Load Balancer") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) @@ -81,19 +81,19 @@ public class ApplicationLoadBalancerResponse extends BaseResponse implements Con private String domainPath; @SerializedName("loadbalancerrule") - @Param(description = "the list of rules associated with the Load Balancer", responseObject = ApplicationLoadBalancerRuleResponse.class) + @Param(description = "The list of rules associated with the Load Balancer", responseObject = ApplicationLoadBalancerRuleResponse.class) private List lbRules; @SerializedName("loadbalancerinstance") - @Param(description = "the list of instances associated with the Load Balancer", responseObject = ApplicationLoadBalancerInstanceResponse.class) + @Param(description = "The list of Instances associated with the Load Balancer", responseObject = ApplicationLoadBalancerInstanceResponse.class) private List lbInstances; @SerializedName(ApiConstants.TAGS) - @Param(description = "the list of resource tags associated with the Load Balancer", responseObject = ResourceTagResponse.class) + @Param(description = "The list of resource tags associated with the Load Balancer", responseObject = ResourceTagResponse.class) private List tags; @SerializedName(ApiConstants.FOR_DISPLAY) - @Param(description = "is rule for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) + @Param(description = "Is rule for display to the regular User", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ApplicationLoadBalancerRuleResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ApplicationLoadBalancerRuleResponse.java index 8af64276c9e1..d6d8c2f95563 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ApplicationLoadBalancerRuleResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ApplicationLoadBalancerRuleResponse.java @@ -30,15 +30,15 @@ @SuppressWarnings("unused") public class ApplicationLoadBalancerRuleResponse extends BaseResponse { @SerializedName(ApiConstants.SOURCE_PORT) - @Param(description = "source port of the load balancer rule") + @Param(description = "Source port of the Load balancer rule") private Integer sourcePort; @SerializedName(ApiConstants.INSTANCE_PORT) - @Param(description = "instance port of the load balancer rule") + @Param(description = "Instance port of the Load balancer rule") private Integer instancePort; @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the load balancer rule") + @Param(description = "The state of the Load balancer rule") private String state; public void setSourcePort(Integer sourcePort) { 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 edbc94bc498e..5be0052069ec 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 @@ -32,55 +32,55 @@ public class AsyncJobResponse extends BaseResponse { @SerializedName("accountid") - @Param(description = "the account id that executed the async command") + @Param(description = "The Account ID that executed the async command") private String accountId; @SerializedName("account") - @Param(description = "the account that executed the async command") + @Param(description = "The Account that executed the async command") private String account; @SerializedName("domainid") - @Param(description = "the domain id that executed the async command") + @Param(description = "The domain ID that executed the async command") private String domainid; @SerializedName("domainpath") - @Param(description = "the domain that executed the async command") + @Param(description = "The domain that executed the async command") private String domainPath; @SerializedName(ApiConstants.USER_ID) - @Param(description = "the user that executed the async command") + @Param(description = "The User that executed the async command") private String userId; @SerializedName("cmd") - @Param(description = "the async command executed") + @Param(description = "The async command executed") private String cmd; @SerializedName("jobprocstatus") - @Param(description = "the progress information of the PENDING job") + @Param(description = "The progress information of the PENDING job") private Integer jobProcStatus; @SerializedName("jobresultcode") - @Param(description = "the result code for the job") + @Param(description = "The result code for the job") private Integer jobResultCode; @SerializedName("jobresulttype") - @Param(description = "the result type") + @Param(description = "The result type") private String jobResultType; @SerializedName("jobresult") - @Param(description = "the result reason") + @Param(description = "The result reason") private ResponseObject jobResult; @SerializedName("jobinstancetype") - @Param(description = "the instance/entity object related to the job") + @Param(description = "The Instance/entity object related to the job") private String jobInstanceType; @SerializedName("jobinstanceid") - @Param(description = "the unique ID of the instance/entity object related to the job") + @Param(description = "The unique ID of the Instance/entity object related to the job") private String jobInstanceId; @SerializedName(ApiConstants.MANAGEMENT_SERVER_ID) - @Param(description = "the msid of the management server on which the job is running", since = "4.19") + @Param(description = "The MSID of the management server on which the job is running", since = "4.19") private String managementServerId; @SerializedName(ApiConstants.MANAGEMENT_SERVER_NAME) @@ -88,11 +88,11 @@ public class AsyncJobResponse extends BaseResponse { private String managementServerName; @SerializedName(ApiConstants.CREATED) - @Param(description = " the created date of the job") + @Param(description = "The created date of the job") private Date created; @SerializedName(ApiConstants.COMPLETED) - @Param(description = " the completed date of the job") + @Param(description = "The completed date of the job") private Date removed; public void setAccountId(String accountId) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/AttachedIsoResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/AttachedIsoResponse.java new file mode 100644 index 000000000000..b259de56218b --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/AttachedIsoResponse.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.response; + +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class AttachedIsoResponse extends BaseResponse { + + @SerializedName("id") + @Param(description = "The ID of the attached ISO") + private String id; + + @SerializedName("name") + @Param(description = "The name of the attached ISO") + private String name; + + @SerializedName("displaytext") + @Param(description = "The display text of the attached ISO") + private String displayText; + + @SerializedName("deviceseq") + @Param(description = "The cdrom slot that holds this ISO (3=hdc, 4=hdd, ...)") + private Integer deviceSeq; + + @SerializedName("bootable") + @Param(description = "Whether this is the bootable ISO for the VM") + private Boolean bootable; + + public AttachedIsoResponse() { + } + + public AttachedIsoResponse(String id, String name, String displayText, Integer deviceSeq, boolean bootable) { + this.id = id; + this.name = name; + this.displayText = displayText; + this.deviceSeq = deviceSeq; + this.bootable = bootable; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getDisplayText() { + return displayText; + } + + public Integer getDeviceSeq() { + return deviceSeq; + } + + public Boolean getBootable() { + return bootable; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/AutoScalePolicyResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/AutoScalePolicyResponse.java index e3f9902dec6a..d2155fb23583 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/AutoScalePolicyResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/AutoScalePolicyResponse.java @@ -31,47 +31,47 @@ public class AutoScalePolicyResponse extends BaseResponse implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the autoscale policy ID") + @Param(description = "The autoscale policy ID") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "name of the autoscale policy") + @Param(description = "Name of the autoscale policy") private String name; @SerializedName(ApiConstants.ACTION) - @Param(description = "the action to be executed if all the conditions evaluate to true for the specified duration.") + @Param(description = "The action to be executed if all the conditions evaluate to true for the specified duration.") private String action; @SerializedName(ApiConstants.DURATION) - @Param(description = "the duration for which the conditions have to be true before action is taken") + @Param(description = "The duration for which the conditions have to be true before action is taken") private Integer duration; @SerializedName(ApiConstants.QUIETTIME) - @Param(description = "the cool down period for which the policy should not be evaluated after the action has been taken") + @Param(description = "The cool down period for which the policy should not be evaluated after the action has been taken") private Integer quietTime; @SerializedName("conditions") - @Param(description = "the list of IDs of the conditions that are being evaluated on every interval") + @Param(description = "The list of IDs of the conditions that are being evaluated on every interval") private List conditions; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account owning the autoscale policy") + @Param(description = "The Account owning the autoscale policy") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id autoscale policy") + @Param(description = "The project id autoscale policy") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the autoscale policy") + @Param(description = "The project name of the autoscale policy") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID of the autoscale policy") + @Param(description = "The domain ID of the autoscale policy") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name of the autoscale policy") + @Param(description = "The domain name of the autoscale policy") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/AutoScaleVmGroupResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/AutoScaleVmGroupResponse.java index b1389acdecb3..921e3f6166a3 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/AutoScaleVmGroupResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/AutoScaleVmGroupResponse.java @@ -32,95 +32,95 @@ public class AutoScaleVmGroupResponse extends BaseResponseWithAnnotations implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the autoscale vm group ID") + @Param(description = "The autoscale Instance group ID") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the autoscale vm group ") + @Param(description = "The name of the autoscale Instance group ") private String name; @SerializedName(ApiConstants.LBID) - @Param(description = "the load balancer rule ID") + @Param(description = "The Load balancer rule ID") private String loadBalancerId; @SerializedName(ApiConstants.ASSOCIATED_NETWORK_NAME) - @Param(description = "the name of the guest network the lb rule belongs to") + @Param(description = "The name of the guest Network the LB rule belongs to") private String networkName; @SerializedName(ApiConstants.ASSOCIATED_NETWORK_ID) - @Param(description = "the id of the guest network the lb rule belongs to") + @Param(description = "The id of the guest Network the LB rule belongs to") private String networkId; @SerializedName(ApiConstants.LB_PROVIDER) - @Param(description = "the lb provider of the guest network the lb rule belongs to") + @Param(description = "The LB provider of the guest Network the LB rule belongs to") private String lbProvider; @SerializedName(ApiConstants.PUBLIC_IP_ID) - @Param(description = "the public ip address id") + @Param(description = "The public IP address ID") private String publicIpId; @SerializedName(ApiConstants.PUBLIC_IP) - @Param(description = "the public ip address") + @Param(description = "The public IP address") private String publicIp; @SerializedName(ApiConstants.PUBLIC_PORT) - @Param(description = "the public port") + @Param(description = "The public port") private String publicPort; @SerializedName(ApiConstants.PRIVATE_PORT) - @Param(description = "the private port") + @Param(description = "The private port") private String privatePort; @SerializedName(ApiConstants.VMPROFILE_ID) - @Param(description = "the autoscale profile that contains information about the vms in the vm group.") + @Param(description = "The autoscale profile that contains information about the Instances in the Instance group.") private String profileId; @SerializedName(ApiConstants.MIN_MEMBERS) - @Param(description = "the minimum number of members in the vmgroup, the number of instances in the vm group will be equal to or more than this number.") + @Param(description = "The minimum number of members in the Instance Group, the number of Instances in the Instance group will be equal to or more than this number.") private int minMembers; @SerializedName(ApiConstants.MAX_MEMBERS) - @Param(description = "the maximum number of members in the vmgroup, The number of instances in the vm group will be equal to or less than this number.") + @Param(description = "The maximum number of members in the Instance Group, The number of Instances in the Instance group will be equal to or less than this number.") private int maxMembers; @SerializedName(ApiConstants.AVAILABLE_VIRTUAL_MACHINE_COUNT) - @Param(description = "the number of available virtual machines (in Running, Starting, Stopping or Migrating state) in the vmgroup", since = "4.18.0") + @Param(description = "The number of available Instances (in Running, Starting, Stopping or Migrating state) in the Instance Group", since = "4.18.0") private int availableVirtualMachineCount; @SerializedName(ApiConstants.INTERVAL) - @Param(description = "the frequency at which the conditions have to be evaluated") + @Param(description = "The frequency at which the conditions have to be evaluated") private int interval; @SerializedName(ApiConstants.STATE) - @Param(description = "the current state of the AutoScale Vm Group") + @Param(description = "The current state of the AutoScale Instance Group") private String state; @SerializedName(ApiConstants.SCALEUP_POLICIES) - @Param(description = "list of scaleup autoscale policies") + @Param(description = "List of scaleup autoscale policies") private List scaleUpPolicies; @SerializedName(ApiConstants.SCALEDOWN_POLICIES) - @Param(description = "list of scaledown autoscale policies") + @Param(description = "List of scaledown autoscale policies") private List scaleDownPolicies; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account owning the vm group") + @Param(description = "The Account owning the Instance group") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the vm group") + @Param(description = "The project id of the Instance group") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the vm group") + @Param(description = "The project name of the Instance group") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID of the vm group") + @Param(description = "The domain ID of the Instance group") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name of the vm group") + @Param(description = "The domain name of the Instance group") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) @@ -128,11 +128,11 @@ public class AutoScaleVmGroupResponse extends BaseResponseWithAnnotations implem private String domainPath; @SerializedName(ApiConstants.FOR_DISPLAY) - @Param(description = "is group for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) + @Param(description = "Is group for display to the regular User", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; @SerializedName(ApiConstants.CREATED) - @Param(description = "the date when this vm group was created") + @Param(description = "The date when this Instance group was created") private Date created; public AutoScaleVmGroupResponse() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/AutoScaleVmProfileResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/AutoScaleVmProfileResponse.java index 22e4eb2288b2..2eda8210fcf8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/AutoScaleVmProfileResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/AutoScaleVmProfileResponse.java @@ -36,29 +36,29 @@ public class AutoScaleVmProfileResponse extends BaseResponse implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the autoscale vm profile ID") + @Param(description = "The autoscale Instance profile ID") private String id; /* Parameters related to deploy virtual machine */ @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the availability zone to be used while deploying a virtual machine") + @Param(description = "The availability zone to be used while deploying an Instance") private String zoneId; @SerializedName(ApiConstants.SERVICE_OFFERING_ID) - @Param(description = "the service offering to be used while deploying a virtual machine") + @Param(description = "The service offering to be used while deploying an Instance") private String serviceOfferingId; @SerializedName(ApiConstants.TEMPLATE_ID) - @Param(description = "the template to be used while deploying a virtual machine") + @Param(description = "The Template to be used while deploying an Instance") private String templateId; @SerializedName(ApiConstants.OTHER_DEPLOY_PARAMS) - @Param(description = "parameters other than zoneId/serviceOfferringId/templateId to be used while deploying a virtual machine") + @Param(description = "Parameters other than zoneId/serviceOfferringId/templateId to be used while deploying an Instance") private Map otherDeployParams; /* Parameters related to destroying a virtual machine */ @SerializedName(ApiConstants.AUTOSCALE_EXPUNGE_VM_GRACE_PERIOD) - @Param(description = "the time allowed for existing connections to get closed before a vm is destroyed") + @Param(description = "The time allowed for existing connections to get closed before an Instance is destroyed") private Integer expungeVmGracePeriod; /* Parameters related to a running virtual machine - monitoring aspects */ @@ -69,7 +69,7 @@ public class AutoScaleVmProfileResponse extends BaseResponse implements Controll private Map counterParams; @SerializedName(ApiConstants.USER_DATA) - @Param(description = "Base64 encoded VM user data") + @Param(description = "Base64 encoded Instance user data") private String userData; @SerializedName(ApiConstants.USER_DATA_ID) @Param(description="the id of userdata used for the VM", since = "4.18.1") @@ -85,33 +85,33 @@ public class AutoScaleVmProfileResponse extends BaseResponse implements Controll private String userDataDetails; @SerializedName(ApiConstants.AUTOSCALE_USER_ID) - @Param(description = "the ID of the user used to launch and destroy the VMs") + @Param(description = "The ID of the User used to launch and destroy the Instances") private String autoscaleUserId; @Parameter(name = ApiConstants.CS_URL, type = CommandType.STRING, - description = "the API URL including port of the CloudStack Management Server example: http://server.cloud.com:8080/client/api?") + description = "The API URL including port of the CloudStack Management Server example: http://server.cloud.com:8080/client/api?") // leaving cloud.com reference above as it serves only as an example private String csUrl; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account owning the instance group") + @Param(description = "The Account owning the Instance group") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id vm profile") + @Param(description = "The project id Instance profile") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the vm profile") + @Param(description = "The project name of the Instance profile") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID of the vm profile") + @Param(description = "The domain ID of the Instance profile") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name of the vm profile") + @Param(description = "The domain name of the Instance profile") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) @@ -119,7 +119,7 @@ public class AutoScaleVmProfileResponse extends BaseResponse implements Controll private String domainPath; @SerializedName(ApiConstants.FOR_DISPLAY) - @Param(description = "is profile for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) + @Param(description = "Is profile for display to the regular User", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; public AutoScaleVmProfileResponse() { 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 0e895fa4e967..69bee63e652f 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 @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.response; import java.util.Date; +import java.util.Map; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; @@ -34,11 +35,11 @@ public class BackupOfferingResponse extends BaseResponse { private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "name for the backup offering") + @Param(description = "Name for the backup offering") private String name; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "description for the backup offering") + @Param(description = "Description for the backup offering") private String description; @SerializedName(ApiConstants.PROVIDER) @@ -46,25 +47,43 @@ public class BackupOfferingResponse extends BaseResponse { private String provider; @SerializedName(ApiConstants.EXTERNAL_ID) - @Param(description = "external ID on the provider side") + @Param(description = "External ID on the provider side") private String externalId; @SerializedName(ApiConstants.ALLOW_USER_DRIVEN_BACKUPS) - @Param(description = "whether offering allows user driven ad-hoc/scheduled backups") + @Param(description = "Whether the offering allows User driven ad-hoc/scheduled backups") private Boolean userDrivenBackups; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "zone ID") + @Param(description = "Zone ID") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "zone name") + @Param(description = "Zone name") private String zoneName; + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "the domain ID(s) this backup offering belongs to.", + since = "4.23.0") + private String domainId; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "the domain name(s) this backup offering belongs to.", + since = "4.23.0") + private String domain; + + @SerializedName(ApiConstants.CROSS_ZONE_INSTANCE_CREATION) + @Param(description = "the backups with this offering can be used to create Instances on all Zones", since = "4.22.0") + private Boolean crossZoneInstanceCreation; + @SerializedName(ApiConstants.CREATED) - @Param(description = "the date this backup offering was created") + @Param(description = "The date this backup offering was created") private Date created; + @SerializedName(ApiConstants.BACKUP_OFFERING_DETAILS) + @Param(description = "Details for the backup offering", since = "4.23.0") + private Map details; + public void setId(String id) { this.id = id; } @@ -97,7 +116,23 @@ public void setZoneName(String zoneName) { this.zoneName = zoneName; } + public void setCrossZoneInstanceCreation(Boolean crossZoneInstanceCreation) { + this.crossZoneInstanceCreation = crossZoneInstanceCreation; + } + public void setCreated(Date created) { this.created = created; } + + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + public void setDetails(Map details) { + this.details = details; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BackupProviderResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BackupProviderResponse.java index 5227d850887c..af74c509b109 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/BackupProviderResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupProviderResponse.java @@ -28,11 +28,11 @@ @EntityReference(BackupProvider.class) public class BackupProviderResponse extends BaseResponse { @SerializedName(ApiConstants.NAME) - @Param(description = "the CA service provider name") + @Param(description = "The CA service provider name") private String name; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "the description of the CA service provider") + @Param(description = "The description of the CA service provider") private String description; public String getName() { 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 0db51f040349..0d3c830950b2 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,18 @@ public class BackupRepositoryResponse extends BaseResponse { @Param(description = "backup type") private String type; + @SerializedName(ApiConstants.MOUNT_OPTIONS) + @Param(description = "mount options", since = "4.22.1") + private String mountOptions; + @SerializedName(ApiConstants.CAPACITY_BYTES) @Param(description = "capacity of the backup repository") private Long capacityBytes; + @SerializedName(ApiConstants.CROSS_ZONE_INSTANCE_CREATION) + @Param(description = "the backups in this repository can be used to create Instances on all Zones") + private Boolean crossZoneInstanceCreation; + @SerializedName("created") @Param(description = "the date and time the backup repository was added") private Date created; @@ -124,6 +132,14 @@ public void setType(String type) { this.type = type; } + public String getMountOptions() { + return mountOptions; + } + + public void setMountOptions(String mountOptions) { + this.mountOptions = mountOptions; + } + public Long getCapacityBytes() { return capacityBytes; } @@ -132,6 +148,14 @@ public void setCapacityBytes(Long capacityBytes) { this.capacityBytes = capacityBytes; } + public Boolean getCrossZoneInstanceCreation() { + return crossZoneInstanceCreation; + } + + public void setCrossZoneInstanceCreation(Boolean crossZoneInstanceCreation) { + this.crossZoneInstanceCreation = crossZoneInstanceCreation; + } + public Date getCreated() { return created; } 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 0ae558ac8033..70db01445edd 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 @@ -32,7 +32,7 @@ public class BackupResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "ID of the VM backup") + @Param(description = "ID of the Instance backup") private String id; @SerializedName(ApiConstants.NAME) @@ -44,71 +44,83 @@ public class BackupResponse extends BaseResponse { private String description; @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) - @Param(description = "ID of the VM") + @Param(description = "ID of the Instance") private String vmId; @SerializedName(ApiConstants.VIRTUAL_MACHINE_NAME) - @Param(description = "name of the VM") + @Param(description = "Name of the Instance") private String vmName; @SerializedName(ApiConstants.EXTERNAL_ID) - @Param(description = "external backup id") + @Param(description = "External backup id") private String externalId; @SerializedName(ApiConstants.TYPE) - @Param(description = "backup type") + @Param(description = "Backup type") private String type; @SerializedName(ApiConstants.CREATED) - @Param(description = "backup date") + @Param(description = "Backup date") private Date date; @SerializedName(ApiConstants.SIZE) - @Param(description = "backup size in bytes") + @Param(description = "Backup size in bytes") private Long size; @SerializedName(ApiConstants.VIRTUAL_SIZE) - @Param(description = "backup protected (virtual) size in bytes") + @Param(description = "Backup protected (virtual) size in bytes") private Long protectedSize; + @SerializedName(ApiConstants.UNCOMPRESSED_SIZE) + @Param(description = "Backup uncompressed size in bytes. Only defined if backup is compressed.") + private Long uncompressedSize; + @SerializedName(ApiConstants.STATUS) - @Param(description = "backup status") + @Param(description = "Backup status") private Backup.Status status; + @SerializedName(ApiConstants.COMPRESSION_STATUS) + @Param(description = "Backup compression status.") + private Backup.CompressionStatus compressionStatus; + + @SerializedName(ApiConstants.VALIDATION_STATUS) + @Param(description = "Backup validation status.") + private Backup.ValidationStatus validationStatus; + @SerializedName(ApiConstants.VOLUMES) - @Param(description = "backed up volumes") + @Param(description = "Backed up volumes") private String volumes; @SerializedName(ApiConstants.BACKUP_OFFERING_ID) - @Param(description = "backup offering id") + @Param(description = "Backup offering id") private String backupOfferingId; @SerializedName(ApiConstants.BACKUP_OFFERING_NAME) - @Param(description = "backup offering name") + @Param(description = "Backup offering name") private String backupOfferingName; @SerializedName(ApiConstants.ACCOUNT_ID) - @Param(description = "account id") + @Param(description = "Account id") private String accountId; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "account name") + @Param(description = "Account name") private String account; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "domain id") + @Param(description = "Domain ID") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "domain name") + @Param(description = "Domain name") private String domain; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "zone id") + @Param(description = "Zone ID") private String zoneId; @SerializedName(ApiConstants.ZONE) - @Param(description = "zone name") + @Param(description = "Zone name") private String zone; @SerializedName(ApiConstants.VM_DETAILS) @@ -123,6 +135,22 @@ public class BackupResponse extends BaseResponse { @Param(description = "The backup offering corresponding to this backup was removed from the VM", since = "4.21.0") private Boolean vmOfferingRemoved; + @SerializedName(ApiConstants.IS_BACKUP_VM_EXPUNGED) + @Param(description = "Indicates whether the VM from which the backup was taken is expunged or not", since = "4.22.0") + private Boolean isVmExpunged; + + @SerializedName(ApiConstants.FROM_CHECKPOINT_ID) + @Param(description = "Previous active checkpoint ID for incremental backups", since = "4.23.0") + private String fromCheckpointId; + + @SerializedName(ApiConstants.TO_CHECKPOINT_ID) + @Param(description = "Next checkpoint ID for incremental backups", since = "4.23.0") + private String toCheckpointId; + + @SerializedName(ApiConstants.HOST_ID) + @Param(description = "Host ID where the backup is running", since = "4.23.0") + private String hostId; + public String getId() { return id; } @@ -203,6 +231,14 @@ public void setProtectedSize(Long protectedSize) { this.protectedSize = protectedSize; } + public Long getUncompressedSize() { + return uncompressedSize; + } + + public void setUncompressedSize(Long uncompressedSize) { + this.uncompressedSize = uncompressedSize; + } + public Backup.Status getStatus() { return status; } @@ -211,6 +247,22 @@ public void setStatus(Backup.Status status) { this.status = status; } + public Backup.CompressionStatus getCompressionStatus() { + return compressionStatus; + } + + public void setCompressionStatus(Backup.CompressionStatus compressionStatus) { + this.compressionStatus = compressionStatus; + } + + public Backup.ValidationStatus getValidationStatus() { + return validationStatus; + } + + public void setValidationStatus(Backup.ValidationStatus validationStatus) { + this.validationStatus = validationStatus; + } + public String getVolumes() { return volumes; } @@ -306,4 +358,32 @@ public Boolean getVmOfferingRemoved() { public void setVmOfferingRemoved(Boolean vmOfferingRemoved) { this.vmOfferingRemoved = vmOfferingRemoved; } + + public void setVmExpunged(Boolean isVmExpunged) { + this.isVmExpunged = isVmExpunged; + } + + public void setFromCheckpointId(String fromCheckpointId) { + this.fromCheckpointId = fromCheckpointId; + } + + public String getFromCheckpointId() { + return this.fromCheckpointId; + } + + public void setToCheckpointId(String toCheckpointId) { + this.toCheckpointId = toCheckpointId; + } + + public String getToCheckpointId() { + return this.toCheckpointId; + } + + public void setHostId(String hostId) { + this.hostId = hostId; + } + + public String getHostId() { + return this.hostId; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BackupRestorePointResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BackupRestorePointResponse.java index 22bb099b1b03..e338fe6b35f9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/BackupRestorePointResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupRestorePointResponse.java @@ -31,15 +31,15 @@ public class BackupRestorePointResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "external id of the restore point") + @Param(description = "External ID of the restore point") private String id; @SerializedName(ApiConstants.CREATED) - @Param(description = "created time") + @Param(description = "Created time") private Date created; @SerializedName(ApiConstants.TYPE) - @Param(description = "restore point type") + @Param(description = "Restore point type") private String type; public String getId() { 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 d8c2980774d3..5da07864603a 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 @@ -33,29 +33,33 @@ public class BackupScheduleResponse extends BaseResponse { private String id; @SerializedName(ApiConstants.VIRTUAL_MACHINE_NAME) - @Param(description = "name of the VM") + @Param(description = "Name of the Instance") private String vmName; @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) - @Param(description = "ID of the VM") + @Param(description = "ID of the Instance") private String vmId; @SerializedName(ApiConstants.SCHEDULE) - @Param(description = "time the backup is scheduled to be taken.") + @Param(description = "The time the backup is scheduled to be taken.") private String schedule; @SerializedName(ApiConstants.INTERVAL_TYPE) - @Param(description = "the interval type of the backup schedule") + @Param(description = "The interval type of the backup schedule") private DateUtil.IntervalType intervalType; @SerializedName(ApiConstants.TIMEZONE) - @Param(description = "the time zone of the backup schedule") + @Param(description = "The time zone of the backup schedule") private String timezone; @SerializedName(ApiConstants.MAX_BACKUPS) @Param(description = "maximum number of backups retained") private Integer maxBackups; + @SerializedName(ApiConstants.ISOLATED) + @Param(description = ApiConstants.PARAMETER_DESCRIPTION_ISOLATED_BACKUPS) + private boolean isolated; + public void setId(String id) { this.id = id; } @@ -111,4 +115,8 @@ public void setMaxBackups(Integer maxBackups) { public void setQuiesceVM(Boolean quiesceVM) { this.quiesceVM = quiesceVM; } + + public void setIsolated(boolean isolated) { + this.isolated = isolated; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BackupServiceJobResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BackupServiceJobResponse.java new file mode 100644 index 000000000000..0d4984fe34f5 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupServiceJobResponse.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.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 java.util.Date; + +public class BackupServiceJobResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "Compression job ID.") + private Long id; + + @SerializedName(ApiConstants.BACKUP_ID) + @Param(description = "Backup ID.") + private String backupId; + + @SerializedName(ApiConstants.HOST_ID) + @Param(description = "Host where the job is being executed.") + private String hostId; + + @SerializedName(ApiConstants.ZONE_ID) + @Param(description = "Zone where the job is being executed.") + private String zoneId; + + @SerializedName(ApiConstants.ATTEMPTS) + @Param(description = "Number of attempts already made to complete this job.") + private Integer attempts; + + @SerializedName(ApiConstants.TYPE) + @Param(description = "Compression job type.") + private String type; + + @SerializedName(ApiConstants.START_DATE) + @Param(description = "Compression job start date.") + private Date startDate; + + @SerializedName(ApiConstants.SCHEDULED_DATE) + @Param(description = "Compression job scheduled start date.") + private Date scheduledDate; + + @SerializedName(ApiConstants.REMOVED) + @Param(description = "Compression job scheduled removed date.") + private Date removed; + + public BackupServiceJobResponse(Long id, String backupId, String zoneId, Integer attempts, String type, Date startDate, Date scheduledDate, Date removed) { + super("backupservicejob"); + this.id = id; + this.backupId = backupId; + this.zoneId = zoneId; + this.attempts = attempts; + this.type = type; + this.startDate = startDate; + this.scheduledDate = scheduledDate; + this.removed = removed; + } + + public void setHostId(String hostId) { + this.hostId = hostId; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BaseRolePermissionResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BaseRolePermissionResponse.java index c39939a20a60..ee563085704b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/BaseRolePermissionResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/BaseRolePermissionResponse.java @@ -27,15 +27,15 @@ public class BaseRolePermissionResponse extends BaseResponse { @SerializedName(ApiConstants.RULE) - @Param(description = "the api name or wildcard rule") + @Param(description = "The api name or wildcard rule") private String rule; @SerializedName(ApiConstants.PERMISSION) - @Param(description = "the permission type of the api name or wildcard rule, allow/deny") + @Param(description = "The permission type of the api name or wildcard rule, allow/deny") private String rulePermission; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "the description of the role permission") + @Param(description = "The description of the role permission") private String ruleDescription; public String getRule() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BaseRoleResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BaseRoleResponse.java index b1bba905bc69..68484149f012 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/BaseRoleResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/BaseRoleResponse.java @@ -25,19 +25,19 @@ public class BaseRoleResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the role") + @Param(description = "The ID of the role") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the role") + @Param(description = "The name of the role") private String roleName; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "the description of the role") + @Param(description = "The description of the role") private String roleDescription; @SerializedName(ApiConstants.IS_PUBLIC) - @Param(description = "Indicates whether the role will be visible to all users (public) or only to root admins (private)." + + @Param(description = "Indicates whether the role will be visible to all Users (public) or only to root admins (private)." + " If this parameter is not specified during the creation of the role its value will be defaulted to true (public).") private boolean publicRole = true; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/CAProviderResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/CAProviderResponse.java index 94d5882e18ac..84d4da5e1037 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/CAProviderResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/CAProviderResponse.java @@ -27,11 +27,11 @@ @EntityReference(value = CAProvider.class) public class CAProviderResponse extends BaseResponse { @SerializedName(ApiConstants.NAME) - @Param(description = "the CA service provider name") + @Param(description = "The CA service provider name") private String name; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "the description of the CA service provider") + @Param(description = "The description of the CA service provider") private String description; public String getName() { 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 eb0daf75148a..7ef627ec33ce 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,8 @@ // under the License. package org.apache.cloudstack.api.response; +import java.util.Map; + import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; @@ -26,71 +28,75 @@ @SuppressWarnings("unused") public class CapabilitiesResponse extends BaseResponse { @SerializedName("securitygroupsenabled") - @Param(description = "true if security groups support is enabled, false otherwise") + @Param(description = "True if security groups support is enabled, false otherwise") private boolean securityGroupsEnabled; @SerializedName("dynamicrolesenabled") - @Param(description = "true if dynamic role-based api checker is enabled, false otherwise") + @Param(description = "True if dynamic role-based api checker is enabled, false otherwise") private boolean dynamicRolesEnabled; @SerializedName("cloudstackversion") - @Param(description = "version of the cloud stack") + @Param(description = "Version of the CloudStack") private String cloudStackVersion; @SerializedName("userpublictemplateenabled") - @Param(description = "true if user and domain admins can set templates to be shared, false otherwise") + @Param(description = "True if user and domain admins can set Templates to be shared, false otherwise") private boolean userPublicTemplateEnabled; @SerializedName("supportELB") - @Param(description = "true if region supports elastic load balancer on basic zones") + @Param(description = "True if region supports elastic Load balancer on basic zones") private String supportELB; @SerializedName(ApiConstants.PROJECT_INVITE_REQUIRED) - @Param(description = "If invitation confirmation is required when add account to project") + @Param(description = "If invitation confirmation is required when add Account to project") private Boolean projectInviteRequired; @SerializedName(ApiConstants.ALLOW_USER_CREATE_PROJECTS) - @Param(description = "true if regular user is allowed to create projects") + @Param(description = "True if regular User is allowed to create projects") private Boolean allowUsersCreateProjects; @SerializedName(ApiConstants.CUSTOM_DISK_OFF_MIN_SIZE) - @Param(description = "minimum size that can be specified when " + "create disk from disk offering with custom size") + @Param(description = "Minimum size that can be specified when " + "create disk from disk offering with custom size") private Long diskOffMinSize; @SerializedName(ApiConstants.CUSTOM_DISK_OFF_MAX_SIZE) - @Param(description = "maximum size that can be specified when " + "create disk from disk offering with custom size") + @Param(description = "Maximum size that can be specified when " + "create disk from disk offering with custom size") private Long diskOffMaxSize; @SerializedName("regionsecondaryenabled") - @Param(description = "true if region wide secondary is enabled, false otherwise") + @Param(description = "True if region wide secondary is enabled, false otherwise") private boolean regionSecondaryEnabled; @SerializedName("apilimitinterval") - @Param(description = "time interval (in seconds) to reset api count") + @Param(description = "Time interval (in seconds) to reset api count") private Integer apiLimitInterval; @SerializedName("kvmsnapshotenabled") - @Param(description = "true if snapshot is supported for KVM host, false otherwise") + @Param(description = "True if Snapshot is supported for KVM host, false otherwise") private boolean kvmSnapshotEnabled; + @SerializedName("snapshotshowchainsize") + @Param(description = "True to show the parent and chain size (sum of physical size of snapshot and all its parents) for incremental snapshots", since = "4.22.1") + private boolean snapshotShowChainSize; + @SerializedName("apilimitmax") @Param(description = "Max allowed number of api requests within the specified interval") private Integer apiLimitMax; @SerializedName("allowuserviewdestroyedvm") - @Param(description = "true if the user is allowed to view destroyed virtualmachines, false otherwise", since = "4.6.0") + @Param(description = "True if the User is allowed to view the destroyed Instances, false otherwise", since = "4.6.0") private boolean allowUserViewDestroyedVM; @SerializedName("allowuserexpungerecovervm") - @Param(description = "true if the user can recover and expunge virtualmachines, false otherwise", since = "4.6.0") + @Param(description = "True if the User can recover and expunge Instances, false otherwise", since = "4.6.0") private boolean allowUserExpungeRecoverVM; @SerializedName("allowuserexpungerecovervolume") - @Param(description = "true if the user can recover and expunge volumes, false otherwise", since = "4.14.0") + @Param(description = "True if the User can recover and expunge volumes, false otherwise", since = "4.14.0") private boolean allowUserExpungeRecoverVolume; @SerializedName("allowuserviewalldomainaccounts") - @Param(description = "true if users can see all accounts within the same domain, false otherwise") + @Param(description = "True if Users can see all Accounts within the same domain, false otherwise") private boolean allowUserViewAllDomainAccounts; @SerializedName(ApiConstants.ALLOW_USER_FORCE_STOP_VM) @@ -98,11 +104,11 @@ public class CapabilitiesResponse extends BaseResponse { private boolean allowUserForceStopVM; @SerializedName("kubernetesserviceenabled") - @Param(description = "true if Kubernetes Service plugin is enabled, false otherwise") + @Param(description = "True if Kubernetes Service plugin is enabled, false otherwise") private boolean kubernetesServiceEnabled; @SerializedName("kubernetesclusterexperimentalfeaturesenabled") - @Param(description = "true if experimental features for Kubernetes cluster such as Docker private registry are enabled, false otherwise") + @Param(description = "True if experimental features for Kubernetes cluster such as Docker private registry are enabled, false otherwise") private boolean kubernetesClusterExperimentalFeaturesEnabled; @SerializedName("customhypervisordisplayname") @@ -110,23 +116,23 @@ public class CapabilitiesResponse extends BaseResponse { private String customHypervisorDisplayName; @SerializedName("defaultuipagesize") - @Param(description = "default page size in the UI for various views, value set in the configurations", since = "4.15.2") + @Param(description = "Default page size in the UI for various views, value set in the configurations", since = "4.15.2") private Long defaultUiPageSize; @SerializedName(ApiConstants.INSTANCES_STATS_RETENTION_TIME) - @Param(description = "the retention time for Instances stats", since = "4.18.0") + @Param(description = "The retention time for Instances stats", since = "4.18.0") private Integer instancesStatsRetentionTime; @SerializedName(ApiConstants.INSTANCES_STATS_USER_ONLY) - @Param(description = "true if stats are collected only for user instances, false if system instance stats are also collected", since = "4.18.0") + @Param(description = "True if stats are collected only for User Instances, false if System VM stats are also collected", since = "4.18.0") private Boolean instancesStatsUserOnly; @SerializedName(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_ENABLED) - @Param(description = "true if stats are retained for instance disks otherwise false", since = "4.18.0") + @Param(description = "True if stats are retained for Instance disks otherwise false", since = "4.18.0") private Boolean instancesDisksStatsRetentionEnabled; @SerializedName(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_TIME) - @Param(description = "the retention time for Instances disks stats", since = "4.18.0") + @Param(description = "The retention time for Instances disks stats", since = "4.18.0") private Integer instancesDisksStatsRetentionTime; @SerializedName(ApiConstants.SHAREDFSVM_MIN_CPU_COUNT) @@ -149,6 +155,14 @@ public class CapabilitiesResponse extends BaseResponse { @Param(description = "true if dynamically scaling for instances is enabled", since = "4.21.0") private Boolean dynamicScalingEnabled; + @SerializedName(ApiConstants.ADDITONAL_CONFIG_ENABLED) + @Param(description = "true if additional configurations or extraconfig can be passed to Instances", since = "4.20.2") + private Boolean additionalConfigEnabled; + + @SerializedName(ApiConstants.VPN_CUSTOMER_GATEWAY_PARAMETERS) + @Param(description = "Excluded and obsolete VPN customer gateway cryptographic parameters") + private Map vpnCustomerGatewayParameters; + public void setSecurityGroupsEnabled(boolean securityGroupsEnabled) { this.securityGroupsEnabled = securityGroupsEnabled; } @@ -193,6 +207,10 @@ public void setKVMSnapshotEnabled(boolean kvmSnapshotEnabled) { this.kvmSnapshotEnabled = kvmSnapshotEnabled; } + public void setSnapshotShowChainSize(boolean snapshotShowChainSize) { + this.snapshotShowChainSize = snapshotShowChainSize; + } + public void setApiLimitInterval(Integer apiLimitInterval) { this.apiLimitInterval = apiLimitInterval; } @@ -272,4 +290,12 @@ public void setExtensionsPath(String extensionsPath) { public void setDynamicScalingEnabled(Boolean dynamicScalingEnabled) { this.dynamicScalingEnabled = dynamicScalingEnabled; } + + public void setAdditionalConfigEnabled(Boolean additionalConfigEnabled) { + this.additionalConfigEnabled = additionalConfigEnabled; + } + + public void setVpnCustomerGatewayParameters(Map vpnCustomerGatewayParameters) { + this.vpnCustomerGatewayParameters = vpnCustomerGatewayParameters; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/CapabilityResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/CapabilityResponse.java index 6862a5541ebe..67a5b33b6a93 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/CapabilityResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/CapabilityResponse.java @@ -26,15 +26,15 @@ public class CapabilityResponse extends BaseResponse { @SerializedName(ApiConstants.NAME) - @Param(description = "the capability name") + @Param(description = "The capability name") private String name; @SerializedName(ApiConstants.VALUE) - @Param(description = "the capability value") + @Param(description = "The capability value") private String value; @SerializedName(ApiConstants.CAN_CHOOSE_SERVICE_CAPABILITY) - @Param(description = "can this service capability value can be choosable while creatine network offerings") + @Param(description = "Can this service capability value can be choosable while creatine Network offerings") private boolean canChoose; public String getName() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/CapacityResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/CapacityResponse.java index 2d0e21586515..e01a7323fee9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/CapacityResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/CapacityResponse.java @@ -24,51 +24,51 @@ public class CapacityResponse extends BaseResponse { @SerializedName(ApiConstants.TYPE) - @Param(description = "the capacity type") + @Param(description = "The capacity type") private Short capacityType; @SerializedName(ApiConstants.NAME) - @Param(description="the capacity name") + @Param(description = "The capacity name") private String capacityName; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the Zone ID") + @Param(description = "The Zone ID") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "the Zone name") + @Param(description = "The Zone name") private String zoneName; @SerializedName(ApiConstants.POD_ID) - @Param(description = "the Pod ID") + @Param(description = "The Pod ID") private String podId; @SerializedName("podname") - @Param(description = "the Pod name") + @Param(description = "The Pod name") private String podName; @SerializedName(ApiConstants.CLUSTER_ID) - @Param(description = "the Cluster ID") + @Param(description = "The Cluster ID") private String clusterId; @SerializedName("clustername") - @Param(description = "the Cluster name") + @Param(description = "The Cluster name") private String clusterName; @SerializedName("capacityallocated") - @Param(description="the capacity currently in allocated") + @Param(description = "The capacity currently in allocated") private Long capacityAllocated; @SerializedName("capacityused") - @Param(description = "the capacity currently in use") + @Param(description = "The capacity currently in use") private Long capacityUsed; @SerializedName("capacitytotal") - @Param(description = "the total capacity available") + @Param(description = "The total capacity available") private Long capacityTotal; @SerializedName("percentused") - @Param(description = "the percentage of capacity currently in use") + @Param(description = "The percentage of capacity currently in use") private String percentUsed; @SerializedName(ApiConstants.TAG) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/CheckpointResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/CheckpointResponse.java new file mode 100644 index 000000000000..2bec7711064f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/CheckpointResponse.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 +//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 org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class CheckpointResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "the checkpoint ID") + private String id; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "the checkpoint creation time") + private Date created; + + @SerializedName(ApiConstants.IS_ACTIVE) + @Param(description = "whether this is the active checkpoint") + private Boolean isActive; + + public void setId(String id) { + this.id = id; + } + + public void setCreated(Date created) { + this.created = created; + } + + public void setIsActive(Boolean isActive) { + this.isActive = isActive; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ChildTemplateResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ChildTemplateResponse.java index 8f5b5de29194..d757b59f0285 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ChildTemplateResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ChildTemplateResponse.java @@ -28,19 +28,19 @@ @SuppressWarnings("unused") public class ChildTemplateResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the template ID") + @Param(description = "The Template ID") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the template name") + @Param(description = "The Template name") private String name; @SerializedName(ApiConstants.SIZE) - @Param(description = "the size of the template") + @Param(description = "The size of the Template") private Integer size; @SerializedName(ApiConstants.TEMPLATE_TYPE) - @Param(description = "the type of the template") + @Param(description = "The type of the Template") private String templateType; public String getId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/CloudIdentifierResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/CloudIdentifierResponse.java index 94520ef2f8f4..e87bdf00c316 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/CloudIdentifierResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/CloudIdentifierResponse.java @@ -26,15 +26,15 @@ public class CloudIdentifierResponse extends BaseResponse { @SerializedName(ApiConstants.USER_ID) - @Param(description = "the user ID for the cloud identifier") + @Param(description = "The User ID for the cloud identifier") private String userId; @SerializedName("cloudidentifier") - @Param(description = "the cloud identifier") + @Param(description = "The cloud identifier") private String cloudIdentifier; @SerializedName("signature") - @Param(description = "the signed response for the cloud identifier") + @Param(description = "The signed response for the cloud identifier") private String signature; public String getUserId() { 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 202ff4bd8705..e73cd3876a92 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 @@ -34,51 +34,51 @@ public class ClusterResponse extends BaseResponseWithAnnotations { private transient long internalId; @SerializedName(ApiConstants.ID) - @Param(description = "the cluster ID") + @Param(description = "The cluster ID") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the cluster name") + @Param(description = "The cluster name") private String name; @SerializedName(ApiConstants.POD_ID) - @Param(description = "the Pod ID of the cluster") + @Param(description = "The Pod ID of the cluster") private String podId; @SerializedName("podname") - @Param(description = "the Pod name of the cluster") + @Param(description = "The Pod name of the cluster") private String podName; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the Zone ID of the cluster") + @Param(description = "The Zone ID of the cluster") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "the Zone name of the cluster") + @Param(description = "The Zone name of the cluster") private String zoneName; @SerializedName("hypervisortype") - @Param(description = "the hypervisor type of the cluster") + @Param(description = "The hypervisor type of the cluster") private String hypervisorType; @SerializedName("clustertype") - @Param(description = "the type of the cluster") + @Param(description = "The type of the cluster") private String clusterType; @SerializedName("allocationstate") - @Param(description = "the allocation state of the cluster") + @Param(description = "The allocation state of the cluster") private String allocationState; @SerializedName("managedstate") - @Param(description = "whether this cluster is managed by cloudstack") + @Param(description = "Whether this cluster is managed by Cloudstack") private String managedState; @SerializedName("capacity") - @Param(description = "the capacity of the Cluster", responseObject = CapacityResponse.class) + @Param(description = "The capacity of the Cluster", responseObject = CapacityResponse.class) private List capacities; @SerializedName("cpuovercommitratio") - @Param(description = "The cpu overcommit ratio of the cluster") + @Param(description = "The CPU overcommit ratio of the cluster") private String cpuovercommitratio; @SerializedName("memoryovercommitratio") diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ConditionResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ConditionResponse.java index 1038177cb86c..d27a1cdf672c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ConditionResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ConditionResponse.java @@ -30,7 +30,7 @@ @SuppressWarnings("unused") public class ConditionResponse extends BaseResponse implements ControlledEntityResponse { @SerializedName("id") - @Param(description = "the id of the Condition") + @Param(description = "The ID of the Condition") private String id; @SerializedName(value = ApiConstants.THRESHOLD) @@ -42,11 +42,11 @@ public class ConditionResponse extends BaseResponse implements ControlledEntityR private String relationalOperator; @SerializedName("counterid") - @Param(description = "the Id of the Counter.") + @Param(description = "The ID of the Counter.") private String counterId; @SerializedName("countername") - @Param(description = "the Name of the Counter.") + @Param(description = "The Name of the Counter.") private String counterName; @SerializedName("counter") @@ -54,11 +54,11 @@ public class ConditionResponse extends BaseResponse implements ControlledEntityR private CounterResponse counterResponse; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain id of the Condition owner") + @Param(description = "The domain ID of the Condition owner") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name of the owner.") + @Param(description = "The domain name of the owner.") private String domain; @SerializedName(ApiConstants.DOMAIN_PATH) @@ -66,19 +66,19 @@ public class ConditionResponse extends BaseResponse implements ControlledEntityR private String domainPath; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "zone id of counter") + @Param(description = "Zone ID of counter") private String zoneId; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the Condition.") + @Param(description = "The project ID of the Condition.") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the Condition") + @Param(description = "The project name of the Condition") private String projectName; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the owner of the Condition.") + @Param(description = "The owner of the Condition.") private String accountName; // ///////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationGroupResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationGroupResponse.java index 053ee5fdc35e..32926a71d8a8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationGroupResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationGroupResponse.java @@ -27,19 +27,19 @@ public class ConfigurationGroupResponse extends BaseResponse { @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the configuration group") + @Param(description = "The name of the configuration group") private String groupName; @SerializedName(ApiConstants.SUBGROUP) - @Param(description = "the subgroups of the configuration group", responseObject = ConfigurationSubGroupResponse.class) + @Param(description = "The subgroups of the configuration group", responseObject = ConfigurationSubGroupResponse.class) private List subGroups; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "the description of the configuration group") + @Param(description = "The description of the configuration group") private String description; @SerializedName(ApiConstants.PRECEDENCE) - @Param(description = "the precedence of the configuration group") + @Param(description = "The precedence of the configuration group") private Long precedence; public String getGroupName() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationResponse.java index 1818e914a97e..416f0102d859 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationResponse.java @@ -25,63 +25,63 @@ public class ConfigurationResponse extends BaseResponse { @SerializedName(ApiConstants.CATEGORY) - @Param(description = "the category of the configuration") + @Param(description = "The category of the configuration") private String category; @SerializedName(ApiConstants.GROUP) - @Param(description = "the group of the configuration", since = "4.18.0") + @Param(description = "The group of the configuration", since = "4.18.0") private String group; @SerializedName(ApiConstants.SUBGROUP) - @Param(description = "the subgroup of the configuration", since = "4.18.0") + @Param(description = "The subgroup of the configuration", since = "4.18.0") private String subGroup; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the configuration") + @Param(description = "The name of the configuration") private String name; @SerializedName(ApiConstants.VALUE) - @Param(description = "the value of the configuration") + @Param(description = "The value of the configuration") private String value; @SerializedName(ApiConstants.DEFAULT_VALUE) - @Param(description = "the default value of the configuration", since = "4.18.0") + @Param(description = "The default value of the configuration", since = "4.18.0") private String defaultValue; @SerializedName(ApiConstants.SCOPE) - @Param(description = "scope(zone/cluster/pool/account) of the parameter that needs to be updated") + @Param(description = "Scope (zone/cluster/pool/account) of the parameter that needs to be updated") private String scope; @SerializedName(ApiConstants.ID) - @Param(description = "the value of the configuration") + @Param(description = "The value of the configuration") private Long id; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "the description of the configuration") + @Param(description = "The description of the configuration") private String description; @SerializedName(ApiConstants.IS_DYNAMIC) - @Param(description = "true if the configuration is dynamic") + @Param(description = "True if the configuration is dynamic") private boolean isDynamic; @SerializedName(ApiConstants.COMPONENT) - @Param(description = "the component of the configuration", since = "4.18.0") + @Param(description = "The component of the configuration", since = "4.18.0") private String component; @SerializedName(ApiConstants.PARENT) - @Param(description = "the name of the parent configuration", since = "4.18.0") + @Param(description = "The name of the parent configuration", since = "4.18.0") private String parent; @SerializedName(ApiConstants.DISPLAY_TEXT) - @Param(description = "the display text of the configuration", since = "4.18.0") + @Param(description = "The display text of the configuration", since = "4.18.0") private String displayText; @SerializedName(ApiConstants.TYPE) - @Param(description = "the type of the configuration value", since = "4.18.0") + @Param(description = "The type of the configuration value", since = "4.18.0") private String type; @SerializedName(ApiConstants.OPTIONS) - @Param(description = "the possible options of the configuration value", since = "4.18.0") + @Param(description = "The possible options of the configuration value", since = "4.18.0") private String options; public String getCategory() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationSubGroupResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationSubGroupResponse.java index fda8e18b361a..81f1bbd45768 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationSubGroupResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationSubGroupResponse.java @@ -25,11 +25,11 @@ public class ConfigurationSubGroupResponse extends BaseResponse { @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the configuration subgroup") + @Param(description = "The name of the configuration subgroup") private String subGroupName; @SerializedName(ApiConstants.PRECEDENCE) - @Param(description = "the precedence of the configuration subgroup") + @Param(description = "The precedence of the configuration subgroup") private Long precedence; public String getSubGroupName() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ConsoleEndpointWebsocketResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ConsoleEndpointWebsocketResponse.java index d98b52d08636..0cc6a53019a6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ConsoleEndpointWebsocketResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ConsoleEndpointWebsocketResponse.java @@ -27,23 +27,23 @@ public ConsoleEndpointWebsocketResponse() { } @SerializedName(ApiConstants.TOKEN) - @Param(description = "the console websocket token") + @Param(description = "The console websocket token") private String token; @SerializedName("host") - @Param(description = "the console websocket host") + @Param(description = "The console websocket host") private String host; @SerializedName(ApiConstants.PORT) - @Param(description = "the console websocket port") + @Param(description = "The console websocket port") private String port; @SerializedName(ApiConstants.PATH) - @Param(description = "the console websocket path") + @Param(description = "The console websocket path") private String path; @SerializedName("extra") - @Param(description = "the console websocket extra field for validation (if enabled)") + @Param(description = "The console websocket extra field for validation (if enabled)") private String extra; public String getToken() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/CounterResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/CounterResponse.java index f013690b64cc..d4b4bed7e2bb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/CounterResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/CounterResponse.java @@ -30,7 +30,7 @@ @EntityReference(value = Counter.class) public class CounterResponse extends BaseResponse { @SerializedName("id") - @Param(description = "the id of the Counter") + @Param(description = "The ID of the Counter") private String id; @SerializedName(value = ApiConstants.NAME) @@ -46,7 +46,7 @@ public class CounterResponse extends BaseResponse { private String value; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "zone id of counter") + @Param(description = "Zone ID of counter") private String zoneId; @SerializedName(value = ApiConstants.PROVIDER) 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 c1e04beee732..557315442e4b 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 @@ -27,19 +27,19 @@ public CreateConsoleEndpointResponse() { } @SerializedName(ApiConstants.SUCCESS) - @Param(description = "true if the console endpoint is generated properly") + @Param(description = "True if the console endpoint is generated properly") private Boolean result; @SerializedName(ApiConstants.DETAILS) - @Param(description = "details in case of an error") + @Param(description = "Details in case of an error") private String details; @SerializedName(ApiConstants.URL) - @Param(description = "the console url") + @Param(description = "The console url") private String url; @SerializedName("websocket") - @Param(description = "the console websocket options") + @Param(description = "The console websocket options") private ConsoleEndpointWebsocketResponse websocketResponse; public Boolean getResult() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/CustomCertificateResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/CustomCertificateResponse.java index 44b732769183..ca6e454db1e8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/CustomCertificateResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/CustomCertificateResponse.java @@ -25,7 +25,7 @@ public class CustomCertificateResponse extends BaseResponse { @SerializedName("message") - @Param(description = "message of the certificate upload operation") + @Param(description = "Message of the certificate upload operation") private String message; public String getResultMessage() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DataCenterGuestIpv6PrefixResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DataCenterGuestIpv6PrefixResponse.java index fd0803f1c581..db4314f5568f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DataCenterGuestIpv6PrefixResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DataCenterGuestIpv6PrefixResponse.java @@ -30,31 +30,31 @@ @EntityReference(value = DataCenterGuestIpv6Prefix.class) public class DataCenterGuestIpv6PrefixResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "id of the guest IPv6 prefix") + @Param(description = "ID of the guest IPv6 prefix") private String id; @SerializedName(ApiConstants.PREFIX) - @Param(description = "guest IPv6 prefix") + @Param(description = "Guest IPv6 prefix") private String prefix; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "id of zone to which the IPv6 prefix belongs to." ) + @Param(description = "ID of zone to which the IPv6 prefix belongs to." ) private String zoneId; @SerializedName(ApiConstants.USED_SUBNETS) - @Param(description = "count of the used IPv6 subnets for the prefix." ) + @Param(description = "Count of the used IPv6 subnets for the prefix." ) private Integer usedSubnets; @SerializedName(ApiConstants.AVAILABLE_SUBNETS) - @Param(description = "count of the available IPv6 subnets for the prefix." ) + @Param(description = "Count of the available IPv6 subnets for the prefix." ) private Integer availableSubnets; @SerializedName(ApiConstants.TOTAL_SUBNETS) - @Param(description = "count of the total IPv6 subnets for the prefix." ) + @Param(description = "Count of the total IPv6 subnets for the prefix." ) private Integer totalSubnets; @SerializedName(ApiConstants.CREATED) - @Param(description = " date when this IPv6 prefix was created." ) + @Param(description = "Date when this IPv6 prefix was created." ) private Date created; public void setId(String id) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateHostStatusResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateHostStatusResponse.java index cc9f2fc366ad..80c787a0e2aa 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateHostStatusResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateHostStatusResponse.java @@ -24,19 +24,19 @@ public class DirectDownloadCertificateHostStatusResponse extends BaseResponse { @SerializedName(ApiConstants.HOST_ID) - @Param(description = "the ID of the host") + @Param(description = "The ID of the host") private String hostId; @SerializedName(ApiConstants.HOST_NAME) - @Param(description = "the name of the host") + @Param(description = "The name of the host") private String hostName; @SerializedName(ApiConstants.STATUS) - @Param(description = "indicates if the certificate has been revoked from the host, failed or skipped") + @Param(description = "Indicates if the certificate has been revoked from the host, failed or skipped") private String status; @SerializedName(ApiConstants.DETAILS) - @Param(description = "indicates the details in case of failure or host skipped") + @Param(description = "Indicates the details in case of failure or host skipped") private String details; public String getHostId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java index f04cba812ba5..480d70d8f881 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java @@ -29,47 +29,47 @@ public class DirectDownloadCertificateResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the direct download certificate id") + @Param(description = "The direct download certificate ID") private String id; @SerializedName(ApiConstants.ALIAS) - @Param(description = "the direct download certificate alias") + @Param(description = "The direct download certificate alias") private String alias; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the zone id where the certificate is uploaded") + @Param(description = "The zone ID where the certificate is uploaded") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "the zone name where the certificate is uploaded") + @Param(description = "The zone name where the certificate is uploaded") private String zoneName; @SerializedName(ApiConstants.VERSION) - @Param(description = "the direct download certificate version") + @Param(description = "The direct download certificate version") private String version; @SerializedName(ApiConstants.CERTIFICATE_SUBJECT) - @Param(description = "the direct download certificate subject") + @Param(description = "The direct download certificate subject") private String subject; @SerializedName(ApiConstants.CERTIFICATE_ISSUER) - @Param(description = "the direct download certificate issuer") + @Param(description = "The direct download certificate issuer") private String issuer; @SerializedName(ApiConstants.CERTIFICATE_VALIDITY) - @Param(description = "the direct download certificate issuer") + @Param(description = "The direct download certificate issuer") private String validity; @SerializedName(ApiConstants.CERTIFICATE_SERIALNUM) - @Param(description = "the direct download certificate serial num") + @Param(description = "The direct download certificate serial num") private String serialNum; @SerializedName(ApiConstants.HYPERVISOR) - @Param(description = "the hypervisor of the hosts where the certificate is uploaded") + @Param(description = "The hypervisor of the hosts where the certificate is uploaded") private String hypervisor; @SerializedName(ApiConstants.HOSTS_MAP) - @Param(description = "the hosts where the certificate is uploaded to", responseObject = HostResponse.class) + @Param(description = "The hosts where the certificate is uploaded to", responseObject = HostResponse.class) private List hostsMap; public String getId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DiskOfferingResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DiskOfferingResponse.java index 0fed9827ef13..528890ea5fae 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DiskOfferingResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DiskOfferingResponse.java @@ -30,27 +30,27 @@ @EntityReference(value = DiskOffering.class) public class DiskOfferingResponse extends BaseResponseWithAnnotations { @SerializedName(ApiConstants.ID) - @Param(description = "unique ID of the disk offering") + @Param(description = "Unique ID of the disk offering") private String id; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID(s) this disk offering belongs to. Ignore this information as it is not currently applicable.") + @Param(description = "The domain ID(s) this disk offering belongs to. Ignore this information as it is not currently applicable.") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name(s) this disk offering belongs to. Ignore this information as it is not currently applicable.") + @Param(description = "The domain name(s) this disk offering belongs to. Ignore this information as it is not currently applicable.") private String domain; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the zone ID(s) this disk offering belongs to. Ignore this information as it is not currently applicable.", since = "4.13.0") + @Param(description = "The zone ID(s) this disk offering belongs to. Ignore this information as it is not currently applicable.", since = "4.13.0") private String zoneId; @SerializedName(ApiConstants.ZONE) - @Param(description = "the zone name(s) this disk offering belongs to. Ignore this information as it is not currently applicable.", since = "4.13.0") + @Param(description = "The zone name(s) this disk offering belongs to. Ignore this information as it is not currently applicable.", since = "4.13.0") private String zone; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the disk offering") + @Param(description = "The name of the disk offering") private String name; @SerializedName(ApiConstants.STATE) @@ -58,106 +58,106 @@ public class DiskOfferingResponse extends BaseResponseWithAnnotations { private String state; @SerializedName(ApiConstants.DISPLAY_TEXT) - @Param(description = "an alternate display text of the disk offering.") + @Param(description = "An alternate display text of the disk offering.") private String displayText; @SerializedName(ApiConstants.DISK_SIZE) - @Param(description = "the size of the disk offering in GB") + @Param(description = "The size of the disk offering in GB") private Long diskSize; @SerializedName(ApiConstants.CREATED) - @Param(description = "the date this disk offering was created") + @Param(description = "The date this disk offering was created") private Date created; @SerializedName("iscustomized") - @Param(description = "true if disk offering uses custom size, false otherwise") + @Param(description = "True if disk offering uses custom size, false otherwise") private Boolean customized; @SerializedName("iscustomizediops") - @Param(description = "true if disk offering uses custom iops, false otherwise") + @Param(description = "True if disk offering uses custom IOPS, false otherwise") private Boolean customizedIops; @SerializedName(ApiConstants.MIN_IOPS) - @Param(description = "the min iops of the disk offering") + @Param(description = "The min IOPS of the disk offering") private Long minIops; @SerializedName(ApiConstants.MAX_IOPS) - @Param(description = "the max iops of the disk offering") + @Param(description = "The max IOPS of the disk offering") private Long maxIops; @SerializedName(ApiConstants.HYPERVISOR_SNAPSHOT_RESERVE) - @Param(description = "Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)", since = "4.4") + @Param(description = "Hypervisor Snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)", since = "4.4") private Integer hypervisorSnapshotReserve; @SerializedName(ApiConstants.TAGS) - @Param(description = "the tags for the disk offering") + @Param(description = "The tags for the disk offering") private String tags; @SerializedName("storagetype") - @Param(description = "the storage type for this disk offering") + @Param(description = "The storage type for this disk offering") private String storageType; - @SerializedName("provisioningtype") @Param(description="provisioning type used to create volumes. Valid values are thin, sparse, fat.", since = "4.4.0") + @SerializedName("provisioningtype") @Param(description = "Provisioning type used to create volumes. Valid values are thin, sparse, fat.", since = "4.4.0") private String provisioningType; @SerializedName("diskBytesReadRate") - @Param(description = "bytes read rate of the disk offering") + @Param(description = "Bytes read rate of the disk offering") private Long bytesReadRate; @SerializedName("diskBytesReadRateMax") - @Param(description = "burst bytes read rate of the disk offering") + @Param(description = "Burst bytes read rate of the disk offering") private Long bytesReadRateMax; @SerializedName("diskBytesReadRateMaxLength") - @Param(description = "length (in seconds) of the burst") + @Param(description = "Length (in seconds) of the burst") private Long bytesReadRateMaxLength; @SerializedName("diskBytesWriteRate") - @Param(description = "bytes write rate of the disk offering") + @Param(description = "Bytes write rate of the disk offering") private Long bytesWriteRate; @SerializedName("diskBytesWriteRateMax") - @Param(description = "burst bytes write rate of the disk offering") + @Param(description = "Burst bytes write rate of the disk offering") private Long bytesWriteRateMax; @SerializedName("diskBytesWriteRateMaxLength") - @Param(description = "length (in seconds) of the burst") + @Param(description = "Length (in seconds) of the burst") private Long bytesWriteRateMaxLength; @SerializedName("diskIopsReadRate") - @Param(description = "io requests read rate of the disk offering") + @Param(description = "I/O requests read rate of the disk offering") private Long iopsReadRate; @SerializedName("diskIopsReadRateMax") - @Param(description = "burst io requests read rate of the disk offering") + @Param(description = "Burst io requests read rate of the disk offering") private Long iopsReadRateMax; @SerializedName("diskIopsReadRateMaxLength") - @Param(description = "length (in second) of the burst") + @Param(description = "Length (in second) of the burst") private Long iopsReadRateMaxLength; @SerializedName("diskIopsWriteRate") - @Param(description = "io requests write rate of the disk offering") + @Param(description = "I/O requests write rate of the disk offering") private Long iopsWriteRate; @SerializedName("diskIopsWriteRateMax") - @Param(description = "burst io requests write rate of the disk offering") + @Param(description = "Burst io requests write rate of the disk offering") private Long iopsWriteRateMax; @SerializedName("diskIopsWriteRateMaxLength") - @Param(description = "length (in seconds) of the burst") + @Param(description = "Length (in seconds) of the burst") private Long iopsWriteRateMaxLength; @SerializedName("cacheMode") - @Param(description = "the cache mode to use for this disk offering. none, writeback or writethrough", since = "4.4") + @Param(description = "The cache mode to use for this disk offering. none, writeback or writethrough", since = "4.4") private String cacheMode; @SerializedName("displayoffering") - @Param(description = "whether to display the offering to the end user or not.") + @Param(description = "Whether to display the offering to the end user or not.") private Boolean displayOffering; @SerializedName("vspherestoragepolicy") - @Param(description = "the vsphere storage policy tagged to the disk offering in case of VMware", since = "4.15") + @Param(description = "The vsphere storage policy tagged to the disk offering in case of VMware", since = "4.15") private String vsphereStoragePolicy; @@ -170,7 +170,7 @@ public class DiskOfferingResponse extends BaseResponseWithAnnotations { private Boolean encrypt; @SerializedName(ApiConstants.DETAILS) - @Param(description = "additional key/value details tied with this disk offering", since = "4.17") + @Param(description = "Additional key/value details tied with this disk offering", since = "4.17") private Map details; @SerializedName(ApiConstants.SUITABLE_FOR_VM) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DnsProviderResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DnsProviderResponse.java new file mode 100644 index 000000000000..f3a571f40639 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/DnsProviderResponse.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.response; + +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 DnsProviderResponse extends BaseResponse { + + @SerializedName(ApiConstants.NAME) + @Param(description = "The name of the DNS provider (e.g. PowerDNS, Cloudflare)") + private String name; + + public DnsProviderResponse(String name) { + this.name = name; + setObjectName("dnsprovider"); // Sets the JSON wrapper name + } + + // Accessors + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DnsRecordResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DnsRecordResponse.java new file mode 100644 index 000000000000..6e2082485512 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/DnsRecordResponse.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 org.apache.cloudstack.api.response; + +import java.util.List; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.dns.DnsRecord; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class DnsRecordResponse extends BaseResponse { + + @SerializedName(ApiConstants.NAME) + @Param(description = "The record name (e.g., www.example.com.)") + private String name; + + @SerializedName(ApiConstants.TYPE) + @Param(description = "The record type (e.g., A, CNAME, TXT)") + private DnsRecord.RecordType type; + + @SerializedName("contents") + @Param(description = "The contents of the record (IP address or target)") + private List contents; + + @SerializedName("ttl") + @Param(description = "Time to live (TTL) in seconds") + private Integer ttl; + + public DnsRecordResponse() { + super(); + setObjectName("dnsrecord"); + } + + // Setters + public void setName(String name) { this.name = name; } + public void setType(DnsRecord.RecordType type) { this.type = type; } + public void setContent(List contents) { this.contents = contents; } + public void setTtl(Integer ttl) { this.ttl = ttl; } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DnsServerResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DnsServerResponse.java new file mode 100644 index 000000000000..fecf49f0f984 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/DnsServerResponse.java @@ -0,0 +1,133 @@ +// 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 org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.dns.DnsServer; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = DnsServer.class) +public class DnsServerResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the DNS server") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the DNS server") + private String name; + + @SerializedName(ApiConstants.URL) + @Param(description = "URL of the DNS server API") + private String url; + + @SerializedName(ApiConstants.PORT) + @Param(description = "The port of the DNS server") + private Integer port; + + @SerializedName(ApiConstants.PROVIDER) + @Param(description = "The provider type of the DNS server") + private String provider; + + @SerializedName(ApiConstants.IS_PUBLIC) + @Param(description = "Is the DNS server publicly available") + private Boolean isPublic; + + @SerializedName(ApiConstants.PUBLIC_DOMAIN_SUFFIX) + @Param(description = "The public domain suffix for the DNS server") + private String publicDomainSuffix; + + @SerializedName(ApiConstants.NAME_SERVERS) + @Param(description = "Name servers entries associated to DNS server") + private List nameServers; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "the account associated with the DNS server") + private String accountName; + + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "the ID of the domain associated with the DNS server") + private String domainId; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "the name of the domain associated with the DNS server") + private String domainName; + + @SerializedName(ApiConstants.STATE) + @Param(description = "The state of the account") + private String state; + + public DnsServerResponse() { + super(); + + } + + public void setId(String id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public void setUrl(String url) { + this.url = url; + } + + public void setProvider(String provider) { + this.provider = provider; + } + + public void setPublic(Boolean value) { + isPublic = value; + } + + public void setPort(Integer port) { + this.port = port; + } + + public void setPublicDomainSuffix(String publicDomainSuffix) { + this.publicDomainSuffix = publicDomainSuffix; + } + + public void setNameServers(List nameServers) { + this.nameServers = nameServers; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + public void setDomainName(String domainName) { + this.domainName = domainName; + } + + public void setState(String state) { + this.state = state; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DnsZoneNetworkMapResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DnsZoneNetworkMapResponse.java new file mode 100644 index 000000000000..84fe46ce5aca --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/DnsZoneNetworkMapResponse.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 org.apache.cloudstack.api.response; + +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 DnsZoneNetworkMapResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "The ID of the mapping") + private String id; + + @SerializedName(ApiConstants.DNS_ZONE_ID) + @Param(description = "The ID of the DNS zone") + private String dnsZoneId; + + @SerializedName(ApiConstants.NETWORK_ID) + @Param(description = "The ID of the Network") + private String networkId; + + @SerializedName("subdomain") + @Param(description = "The sub domain name of the auto-registered DNS record") + private String subDomain; + + public DnsZoneNetworkMapResponse() { + super(); + setObjectName("dnszonenetwork"); + } + + // Setters + public void setId(String id) { + this.id = id; + } + + public void setDnsZoneId(String dnsZoneId) { + this.dnsZoneId = dnsZoneId; + } + + public void setNetworkId(String networkId) { + this.networkId = networkId; + } + + public void setSubDomain(String subDomain) { + this.subDomain = subDomain; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DnsZoneResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DnsZoneResponse.java new file mode 100644 index 000000000000..e179d2fd4b13 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/DnsZoneResponse.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.api.response; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.dns.DnsZone; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = DnsZone.class) +public class DnsZoneResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the DNS zone") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the DNS zone") + private String name; + + @SerializedName("dnsserverid") + @Param(description = "ID of the DNS server this zone belongs to") + private String dnsServerId; + + @SerializedName("dnsservername") + @Param(description = "the name of the DNS server hosting this zone") + private String dnsServerName; + + @SerializedName("dnsserveraccount") + @Param(description = "the account name of the DNS server owner") + private String dnsServerAccountName; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "the account associated with the DNS zone") + private String accountName; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "the name of the domain associated with the DNS zone") + private String domainName; + + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "the ID of the domain associated with the DNS server") + private String domainId; + + @SerializedName(ApiConstants.NETWORK_ID) + @Param(description = "ID of the network this zone is associated with") + private String networkId; + + @SerializedName(ApiConstants.NETWORK_NAME) + @Param(description = "Name of the network this zone is associated with") + private String networkName; + + @SerializedName(ApiConstants.TYPE) + @Param(description = "The type of the zone (Public/Private)") + private DnsZone.ZoneType type; + + @SerializedName(ApiConstants.STATE) + @Param(description = "The state of the zone (Active/Inactive)") + private DnsZone.State state; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "Description for the DNS zone") + private String description; + + public DnsZoneResponse() { + super(); + setObjectName("dnszone"); + } + + public void setName(String name) { + this.name = name; + } + + public void setDnsServerId(String dnsServerId) { + this.dnsServerId = dnsServerId; + } + + public void setNetworkId(String networkId) { + this.networkId = networkId; + } + + public void setNetworkName(String networkName) { + this.networkName = networkName; + } + + public void setType(DnsZone.ZoneType type) { + this.type = type; + } + + public void setState(DnsZone.State state) { + this.state = state; + } + + public void setId(String id) { + this.id = id; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setDnsServerName(String dnsServerName) { + this.dnsServerName = dnsServerName; + } + + public void setDnsServerAccountName(String dnsServerAccountName) { + this.dnsServerAccountName = dnsServerAccountName; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public void setDomainName(String domainName) { + this.domainName = domainName; + } + + public void setDomainId(String domainId) { + this.domainId = domainId; + } + +} 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 e2246a1d531f..453c6b229e97 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 @@ -16,93 +16,93 @@ // under the License. package org.apache.cloudstack.api.response; -import com.google.gson.annotations.SerializedName; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.BaseResponseWithAnnotations; +import org.apache.cloudstack.api.BaseResponseWithTagInformation; import org.apache.cloudstack.api.EntityReference; import com.cloud.domain.Domain; import com.cloud.serializer.Param; - -import java.util.Date; -import java.util.List; -import java.util.Map; +import com.google.gson.annotations.SerializedName; @EntityReference(value = Domain.class) -public class DomainResponse extends BaseResponseWithAnnotations implements ResourceLimitAndCountResponse, SetResourceIconResponse { +public class DomainResponse extends BaseResponseWithTagInformation implements ResourceLimitAndCountResponse, SetResourceIconResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the domain") + @Param(description = "The ID of the domain") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the domain") + @Param(description = "The name of the domain") private String domainName; @SerializedName(ApiConstants.LEVEL) - @Param(description = "the level of the domain") + @Param(description = "The level of the domain") private Integer level; @SerializedName("parentdomainid") - @Param(description = "the domain ID of the parent domain") + @Param(description = "The domain ID of the parent domain") private String parentDomainId; @SerializedName("parentdomainname") - @Param(description = "the domain name of the parent domain") + @Param(description = "The domain name of the parent domain") private String parentDomainName; @SerializedName("haschild") - @Param(description = "whether the domain has one or more sub-domains") + @Param(description = "Whether the domain has one or more sub-domains") private boolean hasChild; @SerializedName(ApiConstants.NETWORK_DOMAIN) - @Param(description = "the network domain") + @Param(description = "The Network domain") private String networkDomain; @SerializedName(ApiConstants.PATH) - @Param(description = "the path of the domain") + @Param(description = "The path of the domain") private String path; - @SerializedName(ApiConstants.STATE) @Param(description="the state of the domain") + @SerializedName(ApiConstants.STATE) @Param(description = "The state of the domain") private String state; - @SerializedName(ApiConstants.CREATED) @Param(description="the date when this domain was created") + @SerializedName(ApiConstants.CREATED) @Param(description = "The date when this domain was created") private Date created; - @SerializedName(ApiConstants.VM_LIMIT) @Param(description="the total number of virtual machines that can be deployed by this domain") + @SerializedName(ApiConstants.VM_LIMIT) @Param(description = "The total number of Instances that can be deployed by this domain") private String vmLimit; - @SerializedName(ApiConstants.VM_TOTAL) @Param(description="the total number of virtual machines deployed by this domain") + @SerializedName(ApiConstants.VM_TOTAL) @Param(description = "The total number of Instances deployed by this domain") private Long vmTotal; - @SerializedName(ApiConstants.VM_AVAILABLE) @Param(description="the total number of virtual machines available for this domain to acquire") + @SerializedName(ApiConstants.VM_AVAILABLE) @Param(description = "The total number of Instances available for this domain to acquire") private String vmAvailable; - @SerializedName(ApiConstants.IP_LIMIT) @Param(description="the total number of public ip addresses this domain can acquire") + @SerializedName(ApiConstants.IP_LIMIT) @Param(description = "The total number of public IP addresses this domain can acquire") private String ipLimit; - @SerializedName(ApiConstants.IP_TOTAL) @Param(description="the total number of public ip addresses allocated for this domain") + @SerializedName(ApiConstants.IP_TOTAL) @Param(description = "The total number of public IP addresses allocated for this domain") private Long ipTotal; - @SerializedName(ApiConstants.IP_AVAILABLE) @Param(description="the total number of public ip addresses available for this domain to acquire") + @SerializedName(ApiConstants.IP_AVAILABLE) @Param(description = "The total number of public IP addresses available for this domain to acquire") private String ipAvailable; - @SerializedName("volumelimit") @Param(description="the total volume which can be used by this domain") + @SerializedName("volumelimit") @Param(description = "The total volume which can be used by this domain") private String volumeLimit; - @SerializedName("volumetotal") @Param(description="the total volume being used by this domain") + @SerializedName("volumetotal") @Param(description = "The total volume being used by this domain") private Long volumeTotal; - @SerializedName("volumeavailable") @Param(description="the total volume available for this domain") + @SerializedName("volumeavailable") @Param(description = "The total volume available for this domain") private String volumeAvailable; - @SerializedName("snapshotlimit") @Param(description="the total number of snapshots which can be stored by this domain") + @SerializedName("snapshotlimit") @Param(description = "The total number of Snapshots which can be stored by this domain") private String snapshotLimit; - @SerializedName("snapshottotal") @Param(description="the total number of snapshots stored by this domain") + @SerializedName("snapshottotal") @Param(description = "The total number of Snapshots stored by this domain") private Long snapshotTotal; - @SerializedName("snapshotavailable") @Param(description="the total number of snapshots available for this domain") + @SerializedName("snapshotavailable") @Param(description = "The total number of Snapshots available for this domain") private String snapshotAvailable; @SerializedName(ApiConstants.BACKUP_LIMIT) @@ -129,58 +129,58 @@ public class DomainResponse extends BaseResponseWithAnnotations implements Resou @Param(description = "the total backup storage space (in GiB) available to the domain", since = "4.21.0") private String backupStorageAvailable; - @SerializedName("templatelimit") @Param(description="the total number of templates which can be created by this domain") + @SerializedName("templatelimit") @Param(description = "The total number of Templates which can be created by this domain") private String templateLimit; - @SerializedName("templatetotal") @Param(description="the total number of templates which have been created by this domain") + @SerializedName("templatetotal") @Param(description = "The total number of Templates which have been created by this domain") private Long templateTotal; - @SerializedName("templateavailable") @Param(description="the total number of templates available to be created by this domain") + @SerializedName("templateavailable") @Param(description = "The total number of Templates available to be created by this domain") private String templateAvailable; - @SerializedName("projectlimit") @Param(description="the total number of projects the domain can own", since="3.0.1") + @SerializedName("projectlimit") @Param(description = "The total number of projects the domain can own", since="3.0.1") private String projectLimit; - @SerializedName("projecttotal") @Param(description="the total number of projects being administrated by this domain", since="3.0.1") + @SerializedName("projecttotal") @Param(description = "The total number of projects being administrated by this domain", since="3.0.1") private Long projectTotal; - @SerializedName("projectavailable") @Param(description="the total number of projects available for administration by this domain", since="3.0.1") + @SerializedName("projectavailable") @Param(description = "The total number of projects available for administration by this domain", since="3.0.1") private String projectAvailable; - @SerializedName("networklimit") @Param(description="the total number of networks the domain can own", since="3.0.1") + @SerializedName("networklimit") @Param(description = "The total number of Networks the domain can own", since="3.0.1") private String networkLimit; - @SerializedName("networktotal") @Param(description="the total number of networks owned by domain", since="3.0.1") + @SerializedName("networktotal") @Param(description = "The total number of Networks owned by domain", since="3.0.1") private Long networkTotal; - @SerializedName("networkavailable") @Param(description="the total number of networks available to be created for this domain", since="3.0.1") + @SerializedName("networkavailable") @Param(description = "The total number of Networks available to be created for this domain", since="3.0.1") private String networkAvailable; - @SerializedName("vpclimit") @Param(description="the total number of vpcs the domain can own", since="4.0.0") + @SerializedName("vpclimit") @Param(description = "The total number of VPCs the domain can own", since="4.0.0") private String vpcLimit; - @SerializedName("vpctotal") @Param(description="the total number of vpcs owned by domain", since="4.0.0") + @SerializedName("vpctotal") @Param(description = "The total number of VPCs owned by domain", since="4.0.0") private Long vpcTotal; - @SerializedName("vpcavailable") @Param(description="the total number of vpcs available to be created for this domain", since="4.0.0") + @SerializedName("vpcavailable") @Param(description = "The total number of VPCs available to be created for this domain", since="4.0.0") private String vpcAvailable; - @SerializedName("cpulimit") @Param(description="the total number of cpu cores the domain can own", since="4.2.0") + @SerializedName("cpulimit") @Param(description = "The total number of CPU cores the domain can own", since="4.2.0") private String cpuLimit; - @SerializedName("cputotal") @Param(description="the total number of cpu cores owned by domain", since="4.2.0") + @SerializedName("cputotal") @Param(description = "The total number of CPU cores owned by domain", since="4.2.0") private Long cpuTotal; - @SerializedName("cpuavailable") @Param(description="the total number of cpu cores available to be created for this domain", since="4.2.0") + @SerializedName("cpuavailable") @Param(description = "The total number of CPU cores available to be created for this domain", since="4.2.0") private String cpuAvailable; - @SerializedName("memorylimit") @Param(description="the total memory (in MB) the domain can own", since="4.2.0") + @SerializedName("memorylimit") @Param(description = "The total memory (in MB) the domain can own", since="4.2.0") private String memoryLimit; - @SerializedName("memorytotal") @Param(description="the total memory (in MB) owned by domain", since="4.2.0") + @SerializedName("memorytotal") @Param(description = "The total memory (in MB) owned by domain", since="4.2.0") private Long memoryTotal; - @SerializedName("memoryavailable") @Param(description="the total memory (in MB) available to be created for this domain", since="4.2.0") + @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") @@ -192,22 +192,22 @@ public class DomainResponse extends BaseResponseWithAnnotations implements Resou @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") + @SerializedName("primarystoragelimit") @Param(description = "The total primary storage space (in GiB) the domain can own", since="4.2.0") private String primaryStorageLimit; - @SerializedName("primarystoragetotal") @Param(description="the total primary storage space (in GiB) owned by domain", since="4.2.0") + @SerializedName("primarystoragetotal") @Param(description = "The total primary storage space (in GiB) owned by domain", since="4.2.0") private Long primaryStorageTotal; - @SerializedName("primarystorageavailable") @Param(description="the total primary storage space (in GiB) available to be used for this domain", since="4.2.0") + @SerializedName("primarystorageavailable") @Param(description = "The total primary storage space (in GiB) available to be used for this domain", since="4.2.0") private String primaryStorageAvailable; - @SerializedName("secondarystoragelimit") @Param(description="the total secondary storage space (in GiB) the domain can own", since="4.2.0") + @SerializedName("secondarystoragelimit") @Param(description = "The total secondary storage space (in GiB) the domain can own", since="4.2.0") private String secondaryStorageLimit; - @SerializedName("secondarystoragetotal") @Param(description="the total secondary storage space (in GiB) owned by domain", since="4.2.0") + @SerializedName("secondarystoragetotal") @Param(description = "The total secondary storage space (in GiB) owned by domain", since="4.2.0") private float secondaryStorageTotal; - @SerializedName("secondarystorageavailable") @Param(description="the total secondary storage space (in GiB) available to be used for this domain", since="4.2.0") + @SerializedName("secondarystorageavailable") @Param(description = "The total secondary storage space (in GiB) available to be used for this domain", since="4.2.0") private String secondaryStorageAvailable; @SerializedName(ApiConstants.BUCKET_LIMIT) @@ -239,7 +239,7 @@ public class DomainResponse extends BaseResponseWithAnnotations implements Resou ResourceIconResponse icon; @SerializedName(ApiConstants.DOMAIN_DETAILS) - @Param(description = "details for the domain") + @Param(description = "Details for the domain") private Map details; @SerializedName(ApiConstants.TAGGED_RESOURCES) @@ -589,4 +589,8 @@ public void setDetails(Map details) { public void setTaggedResourceLimitsAndCounts(List taggedResourceLimitsAndCounts) { this.taggedResources = taggedResourceLimitsAndCounts; } + + public void setTags(Set tags) { + this.tags = tags; + } } 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 4b5e886a16e2..6c94991e8f08 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 @@ -34,155 +34,155 @@ @SuppressWarnings("unused") public class DomainRouterResponse extends BaseResponseWithAnnotations implements ControlledViewEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the id of the router") + @Param(description = "The ID of the router") private String id; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the Zone ID for the router") + @Param(description = "The Zone ID for the router") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "the Zone name for the router") + @Param(description = "The Zone name for the router") private String zoneName; @SerializedName(ApiConstants.DNS1) - @Param(description = "the first DNS for the router") + @Param(description = "The first DNS for the router") private String dns1; @SerializedName(ApiConstants.DNS2) - @Param(description = "the second DNS for the router") + @Param(description = "The second DNS for the router") private String dns2; @SerializedName(ApiConstants.IP6_DNS1) - @Param(description = "the first IPv6 DNS for the router") + @Param(description = "The first IPv6 DNS for the router") private String ip6Dns1; @SerializedName(ApiConstants.IP6_DNS2) - @Param(description = "the second IPv6 DNS for the router") + @Param(description = "The second IPv6 DNS for the router") private String ip6Dns2; @SerializedName("networkdomain") - @Param(description = "the network domain for the router") + @Param(description = "The Network domain for the router") private String networkDomain; @SerializedName(ApiConstants.GATEWAY) - @Param(description = "the gateway for the router") + @Param(description = "The gateway for the router") private String gateway; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the router") + @Param(description = "The name of the router") private String name; @SerializedName(ApiConstants.POD_ID) - @Param(description = "the Pod ID for the router") + @Param(description = "The Pod ID for the router") private String podId; @SerializedName(ApiConstants.POD_NAME) - @Param(description = "the Pod name for the router", since = "4.13.2") + @Param(description = "The Pod name for the router", since = "4.13.2") private String podName; @SerializedName(ApiConstants.HOST_ID) - @Param(description = "the host ID for the router") + @Param(description = "The host ID for the router") private String hostId; @SerializedName("hostname") - @Param(description = "the hostname for the router") + @Param(description = "The hostname for the router") private String hostName; @SerializedName(ApiConstants.HOST_CONTROL_STATE) - @Param(description = "the control state of the host for the router") + @Param(description = "The control state of the host for the router") private String hostControlState; @SerializedName("hypervisor") - @Param(description = "the hypervisor on which the template runs") + @Param(description = "The hypervisor on which the Template runs") private String hypervisor; @SerializedName(ApiConstants.LINK_LOCAL_IP) - @Param(description = "the link local IP address for the router") + @Param(description = "The Control IP address for the router") private String linkLocalIp; @SerializedName(ApiConstants.LINK_LOCAL_MAC_ADDRESS) - @Param(description = "the link local MAC address for the router") + @Param(description = "The link local MAC address for the router") private String linkLocalMacAddress; @SerializedName(ApiConstants.LINK_LOCAL_MAC_NETMASK) - @Param(description = "the link local netmask for the router") + @Param(description = "The link local netmask for the router") private String linkLocalNetmask; @SerializedName(ApiConstants.LINK_LOCAL_NETWORK_ID) - @Param(description = "the ID of the corresponding link local network") + @Param(description = "The ID of the corresponding link local Network") private String linkLocalNetworkId; @SerializedName(ApiConstants.PUBLIC_IP) - @Param(description = "the public IP address for the router") + @Param(description = "The public IP address for the router") private String publicIp; @SerializedName("publicmacaddress") - @Param(description = "the public MAC address for the router") + @Param(description = "The public MAC address for the router") private String publicMacAddress; @SerializedName("publicnetmask") - @Param(description = "the public netmask for the router") + @Param(description = "The public netmask for the router") private String publicNetmask; @SerializedName("publicnetworkid") - @Param(description = "the ID of the corresponding public network") + @Param(description = "The ID of the corresponding public Network") private String publicNetworkId; @SerializedName("guestipaddress") - @Param(description = "the guest IP address for the router") + @Param(description = "The guest IP address for the router") private String guestIpAddress; @SerializedName("guestmacaddress") - @Param(description = "the guest MAC address for the router") + @Param(description = "The guest MAC address for the router") private String guestMacAddress; @SerializedName("guestnetmask") - @Param(description = "the guest netmask for the router") + @Param(description = "The guest netmask for the router") private String guestNetmask; @SerializedName("guestnetworkid") - @Param(description = "the ID of the corresponding guest network") + @Param(description = "The ID of the corresponding guest Network") private String guestNetworkId; @SerializedName("guestnetworkname") - @Param(description = "the name of the corresponding guest network") + @Param(description = "The name of the corresponding guest Network") private String guestNetworkName; @SerializedName(ApiConstants.TEMPLATE_ID) - @Param(description = "the template ID for the router") + @Param(description = "The Template ID for the router") private String templateId; @SerializedName(ApiConstants.TEMPLATE_NAME) - @Param(description = "the template name for the router", since = "4.13.2") + @Param(description = "The Template name for the router", since = "4.13.2") private String templateName; @SerializedName(ApiConstants.CREATED) - @Param(description = "the date and time the router was created") + @Param(description = "The date and time the router was created") private Date created; @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the router") + @Param(description = "The state of the router") private State state; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account associated with the router") + @Param(description = "The Account associated with the router") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the ipaddress") + @Param(description = "The project id of the IP address") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the address") + @Param(description = "The project name of the address") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID associated with the router") + @Param(description = "The domain ID associated with the router") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain associated with the router") + @Param(description = "The domain associated with the router") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) @@ -190,27 +190,27 @@ public class DomainRouterResponse extends BaseResponseWithAnnotations implements private String domainPath; @SerializedName(ApiConstants.SERVICE_OFFERING_ID) - @Param(description = "the ID of the service offering of the virtual machine") + @Param(description = "The ID of the service offering of the Instance") private String serviceOfferingId; @SerializedName("serviceofferingname") - @Param(description = "the name of the service offering of the virtual machine") + @Param(description = "The name of the service offering of the Instance") private String serviceOfferingName; @SerializedName("isredundantrouter") - @Param(description = "if this router is an redundant virtual router") + @Param(description = "If this router is an redundant virtual router") private boolean isRedundantRouter; @SerializedName("redundantstate") - @Param(description = "the state of redundant virtual router") + @Param(description = "The state of redundant virtual router") private String redundantState; @SerializedName("version") - @Param(description = "the version of template") + @Param(description = "The version of Template") private String version; @SerializedName("scriptsversion") - @Param(description = "the version of scripts") + @Param(description = "The version of scripts") private String scriptsVersion; @SerializedName(ApiConstants.VPC_ID) @@ -218,23 +218,23 @@ public class DomainRouterResponse extends BaseResponseWithAnnotations implements private String vpcId; @SerializedName("vpcname") - @Param(description = "the name of VPC the router belongs to") + @Param(description = "The name of VPC the router belongs to") private String vpcName; @SerializedName(ApiConstants.ROLE) - @Param(description = "role of the domain router") + @Param(description = "Role of the domain router") private String role; @SerializedName("nic") - @Param(description = "the list of nics associated with the router", responseObject = NicResponse.class, since = "4.0") + @Param(description = "The list of NICs associated with the router", responseObject = NicResponse.class, since = "4.0") private Set nics; @SerializedName("requiresupgrade") - @Param(description = "true if the router template requires upgrader") + @Param(description = "True if the router Template requires upgrade") private boolean requiresUpgrade; @SerializedName(ApiConstants.HEALTHCHECK_FAILED) - @Param(description = "true if any health checks had failed") + @Param(description = "True if any health checks had failed") private boolean healthChecksFailed; @SerializedName("healthcheckresults") @@ -242,7 +242,7 @@ public class DomainRouterResponse extends BaseResponseWithAnnotations implements List healthCheckResults; @SerializedName("softwareversion") - @Param(description = "the version of the code / software in the router") + @Param(description = "The version of the code / software in the router") private String softwareVersion; @SerializedName(ApiConstants.ARCH) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/EventResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/EventResponse.java index 751d00922f1e..889b3c41c6b7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/EventResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/EventResponse.java @@ -30,43 +30,43 @@ @SuppressWarnings("unused") public class EventResponse extends BaseResponse implements ControlledViewEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the event") + @Param(description = "The ID of the event") private String id; @SerializedName(ApiConstants.USERNAME) - @Param(description = "the name of the user who performed the action (can be different from the account if an admin is performing an action for a user, e.g. starting/stopping a user's virtual machine)") + @Param(description = "The name of the User who performed the action (can be different from the Account if an admin is performing an action for a User, e.g. starting/stopping a User's Instance)") private String username; @SerializedName(ApiConstants.TYPE) - @Param(description = "the type of the event (see event types)") + @Param(description = "The type of the event (see event types)") private String eventType; @SerializedName(ApiConstants.LEVEL) - @Param(description = "the event level (INFO, WARN, ERROR)") + @Param(description = "The event level (INFO, WARN, ERROR)") private String level; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "a brief description of the event") + @Param(description = "A brief description of the event") private String description; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account name for the account that owns the object being acted on in the event (e.g. the owner of the virtual machine, ip address, or security group)") + @Param(description = "The Account name for the Account that owns the object being acted on in the event (e.g. the owner of the Instance, IP address, or security group)") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the ipaddress") + @Param(description = "The project ID of the IP address") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the address") + @Param(description = "The project name of the address") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the id of the account's domain") + @Param(description = "The ID of the Account's domain") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the name of the account's domain") + @Param(description = "The name of the Account's domain") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) @@ -74,31 +74,31 @@ public class EventResponse extends BaseResponse implements ControlledViewEntityR private String domainPath; @SerializedName(ApiConstants.RESOURCE_ID) - @Param(description = "the id of the resource", since = "4.17.0") + @Param(description = "The ID of the resource", since = "4.17.0") private String resourceId; @SerializedName(ApiConstants.RESOURCE_TYPE) - @Param(description = "the type of the resource", since = "4.17.0") + @Param(description = "The type of the resource", since = "4.17.0") private String resourceType; @SerializedName(ApiConstants.RESOURCE_NAME) - @Param(description = "the name of the resource", since = "4.17.0") + @Param(description = "The name of the resource", since = "4.17.0") private String resourceName; @SerializedName(ApiConstants.CREATED) - @Param(description = "the date the event was created") + @Param(description = "The date the event was created") private Date created; @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the event") + @Param(description = "The state of the event") private Event.State state; @SerializedName(ApiConstants.PARENT_ID) - @Param(description = "whether the event is parented") + @Param(description = "Whether the event is parented") private String parentId; @SerializedName(ApiConstants.ARCHIVED) - @Param(description = "whether the event has been archived or not") + @Param(description = "Whether the event has been archived or not") private Boolean archived; public void setId(String id) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ExceptionResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ExceptionResponse.java index 65d4ac333711..d475dee0620f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ExceptionResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ExceptionResponse.java @@ -29,19 +29,19 @@ public class ExceptionResponse extends BaseResponse { @SerializedName("uuidList") - @Param(description = "List of uuids associated with this error") + @Param(description = "List of UUIDs associated with this error") private List idList; @SerializedName("errorcode") - @Param(description = "numeric code associated with this error") + @Param(description = "Numeric code associated with this error") private Integer errorCode; @SerializedName("cserrorcode") - @Param(description = "cloudstack exception error code associated with this error") + @Param(description = "Cloudstack exception error code associated with this error") private Integer csErrorCode; @SerializedName("errortext") - @Param(description = "the text associated with this error") + @Param(description = "The text associated with this error") private String errorText = "Command failed due to Internal Server Error"; public ExceptionResponse() { 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 index fdf1e87df506..911839da405f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ExtensionResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionResponse.java @@ -85,6 +85,12 @@ public class ExtensionResponse extends BaseResponse { @Param(description = "Removal timestamp of the extension, if applicable") private Date removed; + @SerializedName(ApiConstants.RESERVED_RESOURCE_DETAILS) + @Param(description = "Resource detail names as comma separated string that should be reserved and not visible " + + "to end users", + since = "4.22.1") + protected String reservedResourceDetails; + public ExtensionResponse(String id, String name, String description, String type) { this.id = id; this.name = name; @@ -179,4 +185,8 @@ public Date getRemoved() { public void setRemoved(Date removed) { this.removed = removed; } + + public void setReservedResourceDetails(String reservedResourceDetails) { + this.reservedResourceDetails = reservedResourceDetails; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ExternalFirewallResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ExternalFirewallResponse.java index b92288ddb3ff..36e2eabadc59 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ExternalFirewallResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ExternalFirewallResponse.java @@ -25,47 +25,47 @@ public class ExternalFirewallResponse extends NetworkDeviceResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the external firewall") + @Param(description = "The ID of the external firewall") private String id; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the zone ID of the external firewall") + @Param(description = "The zone ID of the external firewall") private String zoneId; @SerializedName(ApiConstants.IP_ADDRESS) - @Param(description = "the management IP address of the external firewall") + @Param(description = "The management IP address of the external firewall") private String ipAddress; @SerializedName(ApiConstants.USERNAME) - @Param(description = "the username that's used to log in to the external firewall") + @Param(description = "The username that's used to log in to the external firewall") private String username; @SerializedName(ApiConstants.PUBLIC_INTERFACE) - @Param(description = "the public interface of the external firewall") + @Param(description = "The public interface of the external firewall") private String publicInterface; @SerializedName(ApiConstants.USAGE_INTERFACE) - @Param(description = "the usage interface of the external firewall") + @Param(description = "The usage interface of the external firewall") private String usageInterface; @SerializedName(ApiConstants.PRIVATE_INTERFACE) - @Param(description = "the private interface of the external firewall") + @Param(description = "The private interface of the external firewall") private String privateInterface; @SerializedName(ApiConstants.PUBLIC_ZONE) - @Param(description = "the public security zone of the external firewall") + @Param(description = "The public security zone of the external firewall") private String publicZone; @SerializedName(ApiConstants.PRIVATE_ZONE) - @Param(description = "the private security zone of the external firewall") + @Param(description = "The private security zone of the external firewall") private String privateZone; @SerializedName(ApiConstants.NUM_RETRIES) - @Param(description = "the number of times to retry requests to the external firewall") + @Param(description = "The number of times to retry requests to the external firewall") private String numRetries; @SerializedName(ApiConstants.TIMEOUT) - @Param(description = "the timeout (in seconds) for requests to the external firewall") + @Param(description = "The timeout (in seconds) for requests to the external firewall") private String timeout; @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ExternalLoadBalancerResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ExternalLoadBalancerResponse.java index fe50b9774288..ae25a2cb627e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ExternalLoadBalancerResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ExternalLoadBalancerResponse.java @@ -25,31 +25,31 @@ public class ExternalLoadBalancerResponse extends NetworkDeviceResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the external load balancer") + @Param(description = "The ID of the external Load balancer") private String id; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the zone ID of the external load balancer") + @Param(description = "The zone ID of the external Load balancer") private String zoneId; @SerializedName(ApiConstants.IP_ADDRESS) - @Param(description = "the management IP address of the external load balancer") + @Param(description = "The management IP address of the external Load balancer") private String ipAddress; @SerializedName(ApiConstants.USERNAME) - @Param(description = "the username that's used to log in to the external load balancer") + @Param(description = "The username that's used to log in to the external Load balancer") private String username; @SerializedName(ApiConstants.PUBLIC_INTERFACE) - @Param(description = "the public interface of the external load balancer") + @Param(description = "The public interface of the external Load balancer") private String publicInterface; @SerializedName(ApiConstants.PRIVATE_INTERFACE) - @Param(description = "the private interface of the external load balancer") + @Param(description = "The private interface of the external Load balancer") private String privateInterface; @SerializedName(ApiConstants.NUM_RETRIES) - @Param(description = "the number of times to retry requests to the external load balancer") + @Param(description = "The number of times to retry requests to the external Load balancer") private String numRetries; @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ExtractResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ExtractResponse.java index 3d22dfe092ce..fc5fd6c7a55f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ExtractResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ExtractResponse.java @@ -27,27 +27,27 @@ public class ExtractResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the id of extracted object") + @Param(description = "The ID of extracted object") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the extracted object") + @Param(description = "The name of the extracted object") private String name; @SerializedName("extractId") - @Param(description = "the upload id of extracted object") + @Param(description = "The upload ID of extracted object") private String uploadId; @SerializedName("uploadpercentage") - @Param(description = "the percentage of the entity uploaded to the specified location") + @Param(description = "The percentage of the entity uploaded to the specified location") private Integer uploadPercent; @SerializedName("status") - @Param(description = "the status of the extraction") + @Param(description = "The status of the extraction") private String status; @SerializedName("accountid") - @Param(description = "the account id to which the extracted object belongs") + @Param(description = "The Account ID to which the extracted object belongs") private String accountId; @SerializedName("resultstring") @@ -55,31 +55,31 @@ public class ExtractResponse extends BaseResponse { private String resultString; @SerializedName(ApiConstants.CREATED) - @Param(description = "the time and date the object was created") + @Param(description = "The time and date the object was created") private Date createdDate; @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the extracted object") + @Param(description = "The state of the extracted object") private String state; @SerializedName("storagetype") - @Param(description = "type of the storage") + @Param(description = "Type of the storage") private String storageType; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "zone ID the object was extracted from") + @Param(description = "Zone ID the object was extracted from") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "zone name the object was extracted from") + @Param(description = "Zone name the object was extracted from") private String zoneName; @SerializedName("extractMode") - @Param(description = "the mode of extraction - upload or download") + @Param(description = "The mode of extraction - upload or download") private String mode; @SerializedName(ApiConstants.URL) - @Param(description = "if mode = upload then url of the uploaded entity. if mode = download the url from which the entity can be downloaded") + @Param(description = "If mode = upload then URL of the uploaded entity. if mode = download the URL from which the entity can be downloaded") private String url; public ExtractResponse() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/FirewallResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/FirewallResponse.java index a9ecdd8e3393..f6cc9e5d9499 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/FirewallResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/FirewallResponse.java @@ -28,63 +28,67 @@ @SuppressWarnings("unused") public class FirewallResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the firewall rule") + @Param(description = "The ID of the firewall rule") private String id; @SerializedName(ApiConstants.PROTOCOL) - @Param(description = "the protocol of the firewall rule") + @Param(description = "The protocol of the firewall rule") private String protocol; @SerializedName(ApiConstants.START_PORT) - @Param(description = "the starting port of firewall rule's port range") + @Param(description = "The starting port of firewall rule's port range") private Integer startPort; @SerializedName(ApiConstants.END_PORT) - @Param(description = "the ending port of firewall rule's port range") + @Param(description = "The ending port of firewall rule's port range") private Integer endPort; @SerializedName(ApiConstants.IP_ADDRESS_ID) - @Param(description = "the public ip address id for the firewall rule") + @Param(description = "The public IP address ID for the firewall rule") private String publicIpAddressId; @SerializedName(ApiConstants.NETWORK_ID) - @Param(description = "the network id of the firewall rule") + @Param(description = "The Network ID of the firewall rule") private String networkId; + @SerializedName(ApiConstants.VPC_ID) + @Param(description = "The VPC ID of the firewall rule") + private String vpcId; + @SerializedName(ApiConstants.IP_ADDRESS) - @Param(description = "the public ip address for the firewall rule") + @Param(description = "The public IP address for the firewall rule") private String publicIpAddress; @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the rule") + @Param(description = "The state of the rule") private String state; @SerializedName(ApiConstants.CIDR_LIST) - @Param(description = "the cidr list to forward traffic from. Multiple entries are separated by a single comma character (,).") + @Param(description = "The CIDR list to forward traffic from. Multiple entries are separated by a single comma character (,).") private String cidrList; @SerializedName(ApiConstants.ICMP_TYPE) - @Param(description = "type of the icmp message being sent") + @Param(description = "Type of the ICMP message being sent") private Integer icmpType; @SerializedName(ApiConstants.ICMP_CODE) - @Param(description = "error code for this icmp message") + @Param(description = "Error code for this ICMP message") private Integer icmpCode; @SerializedName(ApiConstants.TAGS) - @Param(description = "the list of resource tags associated with the rule", responseObject = ResourceTagResponse.class) + @Param(description = "The list of resource tags associated with the rule", responseObject = ResourceTagResponse.class) private List tags; @SerializedName(ApiConstants.FOR_DISPLAY) - @Param(description = "is rule for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) + @Param(description = "Is rule for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; @SerializedName(ApiConstants.DEST_CIDR_LIST) - @Param(description = "the cidr list to forward traffic to. Multiple entries are separated by a single comma character (,).") + @Param(description = "The CIDR list to forward traffic to. Multiple entries are separated by a single comma character (,).") private String destCidr; @SerializedName(ApiConstants.TRAFFIC_TYPE) - @Param(description = "the traffic type for the firewall rule", since = "4.17.0") + @Param(description = "The traffic type for the firewall rule", since = "4.17.0") private String trafficType; public void setId(String id) { @@ -115,6 +119,10 @@ public void setNetworkId(String networkId) { this.networkId = networkId; } + public void setVpcId(String vpcId) { + this.vpcId = vpcId; + } + public void setState(String state) { this.state = state; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/FirewallRuleResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/FirewallRuleResponse.java index 1d3b665634fd..aed56a369089 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/FirewallRuleResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/FirewallRuleResponse.java @@ -31,71 +31,75 @@ @SuppressWarnings("unused") public class FirewallRuleResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the port forwarding rule") + @Param(description = "The ID of the port forwarding rule") private String id; @SerializedName(ApiConstants.PRIVATE_START_PORT) - @Param(description = "the starting port of port forwarding rule's private port range") + @Param(description = "The starting port of port forwarding rule's private port range") private String privateStartPort; @SerializedName(ApiConstants.PRIVATE_END_PORT) - @Param(description = "the ending port of port forwarding rule's private port range") + @Param(description = "The ending port of port forwarding rule's private port range") private String privateEndPort; @SerializedName(ApiConstants.PROTOCOL) - @Param(description = "the protocol of the port forwarding rule") + @Param(description = "The protocol of the port forwarding rule") private String protocol; @SerializedName(ApiConstants.PUBLIC_START_PORT) - @Param(description = "the starting port of port forwarding rule's public port range") + @Param(description = "The starting port of port forwarding rule's public port range") private String publicStartPort; @SerializedName(ApiConstants.PUBLIC_END_PORT) - @Param(description = "the ending port of port forwarding rule's private port range") + @Param(description = "The ending port of port forwarding rule's private port range") private String publicEndPort; @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) - @Param(description = "the VM ID for the port forwarding rule") + @Param(description = "The Instance ID for the port forwarding rule") private String virtualMachineId; @SerializedName("virtualmachinename") - @Param(description = "the VM name for the port forwarding rule") + @Param(description = "The Instance name for the port forwarding rule") private String virtualMachineName; @SerializedName("virtualmachinedisplayname") - @Param(description = "the VM display name for the port forwarding rule") + @Param(description = "The Instance display name for the port forwarding rule") private String virtualMachineDisplayName; @SerializedName(ApiConstants.IP_ADDRESS_ID) - @Param(description = "the public ip address id for the port forwarding rule") + @Param(description = "The public IP address id for the port forwarding rule") private String publicIpAddressId; @SerializedName(ApiConstants.IP_ADDRESS) - @Param(description = "the public ip address for the port forwarding rule") + @Param(description = "The public IP address for the port forwarding rule") private String publicIpAddress; @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the rule") + @Param(description = "The state of the rule") private String state; @SerializedName(ApiConstants.CIDR_LIST) - @Param(description = "the cidr list to forward traffic from. Multiple entries are separated by a single comma character (,).") + @Param(description = "The CIDR list to forward traffic from. Multiple entries are separated by a single comma character (,).") private String cidrList; @SerializedName(ApiConstants.TAGS) - @Param(description = "the list of resource tags associated with the rule", responseObject = ResourceTagResponse.class) + @Param(description = "The list of resource tags associated with the rule", responseObject = ResourceTagResponse.class) private List tags; @SerializedName(ApiConstants.VM_GUEST_IP) - @Param(description = "the vm ip address for the port forwarding rule") + @Param(description = "The Instance IP address for the port forwarding rule") private String destNatVmIp; @SerializedName(ApiConstants.NETWORK_ID) - @Param(description = "the id of the guest network the port forwarding rule belongs to") + @Param(description = "The ID of the guest Network the port forwarding rule belongs to") private String networkId; + @SerializedName(ApiConstants.NETWORK_NAME) + @Param(description = "The Name of the guest Network the port forwarding rule belongs to") + private String networkName; + @SerializedName(ApiConstants.FOR_DISPLAY) - @Param(description = "is firewall for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) + @Param(description = "Is firewall for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; public String getDestNatVmIp() { @@ -223,6 +227,10 @@ public void setNetworkId(String networkId) { this.networkId = networkId; } + public void setNetworkName(String networkName) { + this.networkName = networkName; + } + public void setForDisplay(Boolean forDisplay) { this.forDisplay = forDisplay; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GetUploadParamsResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GetUploadParamsResponse.java index b9be06ecc8f8..0e51aa6cd381 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/GetUploadParamsResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/GetUploadParamsResponse.java @@ -30,7 +30,7 @@ public class GetUploadParamsResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the template/volume ID") + @Param(description = "The Template/volume ID") private UUID id; @SerializedName(ApiConstants.POST_URL) @@ -38,15 +38,15 @@ public class GetUploadParamsResponse extends BaseResponse { private URL postURL; @SerializedName(ApiConstants.METADATA) - @Param(description = "encrypted data to be sent in the POST request.") + @Param(description = "Encrypted data to be sent in the POST request.") private String metadata; @SerializedName(ApiConstants.EXPIRES) - @Param(description = "the timestamp after which the signature expires") + @Param(description = "The timestamp after which the signature expires") private String expires; @SerializedName(ApiConstants.SIGNATURE) - @Param(description = "signature to be sent in the POST request.") + @Param(description = "Signature to be sent in the POST request.") private String signature; public GetUploadParamsResponse(UUID id, URL postURL, String metadata, String expires, String signature) { @@ -62,6 +62,10 @@ public GetUploadParamsResponse() { setObjectName("getuploadparams"); } + public UUID getId() { + return id; + } + public void setId(UUID id) { this.id = id; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GetVMPasswordResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GetVMPasswordResponse.java index 49603212edc9..9c9e41774958 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/GetVMPasswordResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/GetVMPasswordResponse.java @@ -25,7 +25,7 @@ public class GetVMPasswordResponse extends BaseResponse { @SerializedName("encryptedpassword") - @Param(description = "The base64 encoded encrypted password of the VM", isSensitive = true) + @Param(description = "The base64 encoded encrypted password of the Instance", isSensitive = true) private String encryptedPassword; public GetVMPasswordResponse() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GlobalLoadBalancerResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GlobalLoadBalancerResponse.java index d41e4d2ab34c..b16706bb872d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/GlobalLoadBalancerResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/GlobalLoadBalancerResponse.java @@ -31,27 +31,27 @@ public class GlobalLoadBalancerResponse extends BaseResponse implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "global load balancer rule ID") + @Param(description = "Global Load balancer rule ID") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "name of the global load balancer rule") + @Param(description = "Name of the global Load balancer rule") private String name; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "the description of the global load balancer rule") + @Param(description = "The description of the global Load balancer rule") private String description; @SerializedName(ApiConstants.GSLB_SERVICE_DOMAIN_NAME) - @Param(description = "DNS domain name given for the global load balancer") + @Param(description = "DNS domain name given for the global Load balancer") private String gslbDomainName; @SerializedName(ApiConstants.GSLB_LB_METHOD) - @Param(description = "Load balancing method used for the global load balancer") + @Param(description = "Load balancing method used for the global Load balancer") private String algorithm; @SerializedName(ApiConstants.GSLB_STICKY_SESSION_METHOD) - @Param(description = "session persistence method used for the global load balancer") + @Param(description = "Session persistence method used for the global Load balancer") private String stickyMethod; @SerializedName(ApiConstants.GSLB_SERVICE_TYPE) @@ -59,27 +59,27 @@ public class GlobalLoadBalancerResponse extends BaseResponse implements Controll private String serviceType; @SerializedName(ApiConstants.REGION_ID) - @Param(description = "Region Id in which global load balancer is created") + @Param(description = "Region ID in which global Load balancer is created") private Integer regionId; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account of the load balancer rule") + @Param(description = "The Account of the Load balancer rule") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the load balancer") + @Param(description = "The project ID of the Load balancer") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the load balancer") + @Param(description = "The project name of the Load balancer") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID of the load balancer rule") + @Param(description = "The domain ID of the Load balancer rule") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain of the load balancer rule") + @Param(description = "The domain of the Load balancer rule") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) @@ -87,7 +87,7 @@ public class GlobalLoadBalancerResponse extends BaseResponse implements Controll private String domainPath; @SerializedName(ApiConstants.LOAD_BALANCER_RULE) - @Param(description = "List of load balancer rules that are part of GSLB rule", responseObject = LoadBalancerResponse.class) + @Param(description = "List of Load balancer rules that are part of GSLB rule", responseObject = LoadBalancerResponse.class) private List siteLoadBalancers; public void setRegionIdId(Integer regionId) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GpuResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GpuResponse.java index b655749126e7..8738c622eaf5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/GpuResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/GpuResponse.java @@ -32,7 +32,7 @@ public class GpuResponse extends BaseResponse { private String gpuGroupName; @SerializedName(ApiConstants.VGPU) - @Param(description = "the list of enabled vGPUs", responseObject = VgpuResponse.class) + @Param(description = "The list of enabled vGPUs", responseObject = VgpuResponse.class) private List vgpu; public void setGpuGroupName(String gpuGroupName) { 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 495e349856ab..182e74200070 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 @@ -30,11 +30,11 @@ @EntityReference(value = GuestOsCategory.class) public class GuestOSCategoryResponse extends BaseResponse implements SetResourceIconResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the OS category") + @Param(description = "The ID of the OS category") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the OS category") + @Param(description = "The name of the OS category") private String name; @SerializedName(ApiConstants.IS_FEATURED) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GuestOSResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GuestOSResponse.java index f870a2f0d942..6f8e7937d0b9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/GuestOSResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/GuestOSResponse.java @@ -28,19 +28,19 @@ @EntityReference(value = GuestOS.class) public class GuestOSResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the OS type") + @Param(description = "The ID of the OS type") private String id; @SerializedName(ApiConstants.OS_CATEGORY_ID) - @Param(description = "the ID of the OS category") + @Param(description = "The ID of the OS category") private String osCategoryId; @SerializedName(ApiConstants.OS_CATEGORY_NAME) - @Param(description = "the name of the OS category") + @Param(description = "The name of the OS category") private String osCategoryName; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the OS type") + @Param(description = "The name of the OS type") private String name; /** @@ -48,15 +48,15 @@ public class GuestOSResponse extends BaseResponse { */ @Deprecated(since = "4.19") @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "the name/description of the OS type") + @Param(description = "The name/description of the OS type") private String description; @SerializedName(ApiConstants.IS_USER_DEFINED) - @Param(description = "is the guest OS user defined") + @Param(description = "Is the guest OS user defined") private Boolean isUserDefined; @SerializedName(ApiConstants.FOR_DISPLAY) - @Param(description = "is the guest OS visible for the users") + @Param(description = "Is the guest OS visible for the users") private Boolean forDisplay; public String getId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GuestOsMappingResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GuestOsMappingResponse.java index 583768d47bef..3681f182e95f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/GuestOsMappingResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/GuestOsMappingResponse.java @@ -29,31 +29,31 @@ @EntityReference(value = GuestOSHypervisor.class) public class GuestOsMappingResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the Guest OS mapping") + @Param(description = "The ID of the Guest OS mapping") private String id; @SerializedName(ApiConstants.HYPERVISOR) - @Param(description = "the hypervisor") + @Param(description = "The hypervisor") private String hypervisor; @SerializedName(ApiConstants.HYPERVISOR_VERSION) - @Param(description = "version of the hypervisor for mapping") + @Param(description = "Version of the hypervisor for mapping") private String hypervisorVersion; @SerializedName(ApiConstants.OS_TYPE_ID) - @Param(description = "the ID of the Guest OS type") + @Param(description = "The ID of the Guest OS type") private String osTypeId; @SerializedName(ApiConstants.OS_DISPLAY_NAME) - @Param(description = "standard display name for the Guest OS") + @Param(description = "Standard display name for the Guest OS") private String osStdName; @SerializedName(ApiConstants.OS_NAME_FOR_HYPERVISOR) - @Param(description = "hypervisor specific name for the Guest OS") + @Param(description = "Hypervisor specific name for the Guest OS") private String osNameForHypervisor; @SerializedName(ApiConstants.IS_USER_DEFINED) - @Param(description = "is the mapping user defined") + @Param(description = "Is the mapping user defined") private String isUserDefined; public String getIsUserDefined() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GuestVlanRangeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GuestVlanRangeResponse.java index 133338d27ae3..c67b38866989 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/GuestVlanRangeResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/GuestVlanRangeResponse.java @@ -29,19 +29,19 @@ @SuppressWarnings("unused") public class GuestVlanRangeResponse extends BaseResponse implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the guest VLAN range") + @Param(description = "The ID of the guest VLAN range") private String id; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account of the guest VLAN range") + @Param(description = "The Account of the guest VLAN range") private String accountName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID of the guest VLAN range") + @Param(description = "The domain ID of the guest VLAN range") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name of the guest VLAN range") + @Param(description = "The domain name of the guest VLAN range") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) @@ -49,23 +49,23 @@ public class GuestVlanRangeResponse extends BaseResponse implements ControlledEn private String domainPath; @SerializedName(ApiConstants.GUEST_VLAN_RANGE) - @Param(description = "the guest VLAN range") + @Param(description = "The guest VLAN range") private String guestVlanRange; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the guest vlan range") + @Param(description = "The project id of the guest VLAN range") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the guest vlan range") + @Param(description = "The project name of the guest VLAN range") private String projectName; @SerializedName(ApiConstants.PHYSICAL_NETWORK_ID) - @Param(description = "the physical network of the guest vlan range") + @Param(description = "The physical Network of the guest VLAN range") private Long physicalNetworkId; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the zone of the guest vlan range") + @Param(description = "The zone of the guest VLAN range") private Long zoneId; public void setId(String id) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GuestVlanResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GuestVlanResponse.java index 6716911ab82e..28d33e9fe659 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/GuestVlanResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/GuestVlanResponse.java @@ -30,23 +30,23 @@ public class GuestVlanResponse extends BaseResponse implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the guest VLAN id") + @Param(description = "The guest VLAN ID") private long id; @SerializedName(ApiConstants.VLAN) - @Param(description = "the guest VLAN") + @Param(description = "The guest VLAN") private String guestVlan; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account of the guest VLAN range") + @Param(description = "The Account of the guest VLAN range") private String accountName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID of the guest VLAN range") + @Param(description = "The domain ID of the guest VLAN range") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name of the guest VLAN range") + @Param(description = "The domain name of the guest VLAN range") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) @@ -54,43 +54,43 @@ public class GuestVlanResponse extends BaseResponse implements ControlledEntityR private String domainPath; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the guest VLAN range") + @Param(description = "The project ID of the guest VLAN range") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the guest VLAN range") + @Param(description = "The project name of the guest VLAN range") private String projectName; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the zone ID of the guest VLAN range") + @Param(description = "The zone ID of the guest VLAN range") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "the zone name of the guest VLAN range") + @Param(description = "The zone name of the guest VLAN range") private String zoneName; @SerializedName(ApiConstants.PHYSICAL_NETWORK_ID) - @Param(description = "the physical network ID of the guest VLAN range") + @Param(description = "The physical Network ID of the guest VLAN range") private String physicalNetworkId; @SerializedName(ApiConstants.PHYSICAL_NETWORK_NAME) - @Param(description = "the physical network name of the guest VLAN range") + @Param(description = "The physical Network name of the guest VLAN range") private String physicalNetworkName; @SerializedName(ApiConstants.IS_DEDICATED) - @Param(description = "true if the guest VLAN is dedicated to the account") + @Param(description = "True if the guest VLAN is dedicated to the Account") private Boolean isDedicated; @SerializedName(ApiConstants.ALLOCATION_STATE) - @Param(description = "the allocation state of the guest VLAN") + @Param(description = "The allocation state of the guest VLAN") private String allocationState; @SerializedName(ApiConstants.TAKEN) - @Param(description = "date the guest VLAN was taken") + @Param(description = "Date the guest VLAN was taken") private Date taken; @SerializedName(ApiConstants.NETWORK) - @Param(description = "the list of networks who use this guest VLAN", responseObject = NetworkResponse.class) + @Param(description = "The list of Networks who use this guest VLAN", responseObject = NetworkResponse.class) private List networks; public void setId(long id) { 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 index fe8a85b4176e..3c53bdb9d93d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java @@ -52,6 +52,11 @@ public class GuiThemeResponse extends BaseResponse { @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.LOGIN_BASE_DOMAIN) + @Param(description = "The ACS domain to be used as base for the login when accessing the GUI through the common name defined in the theme. If a " + + "common name is not defined, this parameter is ignored on the GUI.") + private String loginBaseDomain; + @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; @@ -176,4 +181,12 @@ public void setRecursiveDomains(Boolean recursiveDomains) { public void setRemoved(Date removed) { this.removed = removed; } + + public String getLoginBaseDomain() { + return loginBaseDomain; + } + + public void setLoginBaseDomain(String loginBaseDomain) { + this.loginBaseDomain = loginBaseDomain; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/HAProviderResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HAProviderResponse.java index d75cbc3e1205..c1c761780084 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/HAProviderResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/HAProviderResponse.java @@ -29,11 +29,11 @@ @EntityReference(value = HAConfig.class) public final class HAProviderResponse extends BaseResponse { @SerializedName(ApiConstants.HA_PROVIDER) - @Param(description = "the HA provider") + @Param(description = "The HA provider") private String provider; @SerializedName(ApiConstants.TYPE) - @Param(description = "the HA provider resource type detail") + @Param(description = "The HA provider resource type detail") private List supportedResourceTypes; public HAProviderResponse() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/HSMProfileResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HSMProfileResponse.java new file mode 100644 index 000000000000..7576c76bef15 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/HSMProfileResponse.java @@ -0,0 +1,254 @@ +// 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.Map; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.kms.HSMProfile; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = HSMProfile.class) +public class HSMProfileResponse extends BaseResponse implements ControlledViewEntityResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "the ID of the HSM profile") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "the name of the HSM profile") + private String name; + + @SerializedName(ApiConstants.PROTOCOL) + @Param(description = "the protocol of the HSM profile") + private String protocol; + + @SerializedName(ApiConstants.ACCOUNT_ID) + @Param(description = "the account ID of the HSM profile owner") + private String accountId; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "the account name of the HSM profile owner") + private String accountName; + + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "the domain ID of the HSM profile owner") + private String domainId; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "the domain name of the HSM profile owner") + private String domainName; + + @SerializedName(ApiConstants.DOMAIN_PATH) + @Param(description = "the domain path of the HSM profile owner") + private String domainPath; + + @SerializedName(ApiConstants.PROJECT_ID) + @Param(description = "the project ID of the HSM profile owner") + private String projectId; + + @SerializedName(ApiConstants.PROJECT) + @Param(description = "the project name of the HSM profile owner") + private String projectName; + + @SerializedName(ApiConstants.ZONE_ID) + @Param(description = "the zone ID where the HSM profile is available") + private String zoneId; + + @SerializedName(ApiConstants.ZONE_NAME) + @Param(description = "the zone name where the HSM profile is available") + private String zoneName; + + @SerializedName("vendor") + @Param(description = "the vendor name of the HSM profile") + private String vendorName; + + @SerializedName(ApiConstants.STATE) + @Param(description = "the state of the HSM profile") + private String state; + + @SerializedName(ApiConstants.ENABLED) + @Param(description = "whether the HSM profile is enabled") + private Boolean enabled; + + @SerializedName(ApiConstants.IS_PUBLIC) + @Param(description = "whether this is a system HSM profile available to all users globally") + private Boolean isPublic; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "the date the HSM profile was created") + private Date created; + + @SerializedName(ApiConstants.DETAILS) + @Param(description = "HSM configuration details (sensitive values are encrypted)") + private Map details; + + public void setId(String id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } + + @Override + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + @Override + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + @Override + public void setDomainName(String domainName) { + this.domainName = domainName; + } + + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + + @Override + public void setProjectId(String projectId) { + this.projectId = projectId; + } + + @Override + public void setProjectName(String projectName) { + this.projectName = projectName; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } + + public void setZoneName(String zoneName) { + this.zoneName = zoneName; + } + + public void setVendorName(String vendorName) { + this.vendorName = vendorName; + } + + public void setState(String state) { + this.state = state; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public void setIsPublic(Boolean isPublic) { + this.isPublic = isPublic; + } + + public void setCreated(Date created) { + this.created = created; + } + + public void setDetails(Map details) { + this.details = details; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getProtocol() { + return protocol; + } + + public String getAccountId() { + return accountId; + } + + public String getAccountName() { + return accountName; + } + + public String getDomainId() { + return domainId; + } + + public String getDomainName() { + return domainName; + } + + public String getDomainPath() { + return domainPath; + } + + public String getProjectId() { + return projectId; + } + + public String getProjectName() { + return projectName; + } + + public String getZoneId() { + return zoneId; + } + + public String getZoneName() { + return zoneName; + } + + public String getVendorName() { + return vendorName; + } + + public String getState() { + return state; + } + + public Boolean getEnabled() { + return enabled; + } + + public Boolean getPublic() { + return isPublic; + } + + public Date getCreated() { + return created; + } + + public Map getDetails() { + return details; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/HostForMigrationResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HostForMigrationResponse.java index b4de48baec46..b3853d9cc245 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/HostForMigrationResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/HostForMigrationResponse.java @@ -26,7 +26,7 @@ public class HostForMigrationResponse extends HostResponse { @SerializedName("requiresStorageMotion") - @Param(description = "true if migrating a vm to this host requires storage motion, false otherwise") + @Param(description = "True if migrating an Instance to this host requires storage motion, false otherwise") private Boolean requiresStorageMotion; public void setRequiresStorageMotion(Boolean requiresStorageMotion) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/HostHAResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HostHAResponse.java index a8b44bd56496..de2281dba315 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/HostHAResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/HostHAResponse.java @@ -27,23 +27,23 @@ @EntityReference(value = HAConfig.class) public final class HostHAResponse extends BaseResponse { @SerializedName(ApiConstants.HOST_ID) - @Param(description = "the ID of the host") + @Param(description = "The ID of the host") private String id; @SerializedName(ApiConstants.HA_ENABLE) - @Param(description = "if host HA is enabled for the host") + @Param(description = "If host HA is enabled for the host") private Boolean enabled; @SerializedName(ApiConstants.HA_STATE) - @Param(description = "the HA state of the host") + @Param(description = "The HA state of the host") private HAConfig.HAState haState; @SerializedName(ApiConstants.HA_PROVIDER) - @Param(description = "the host HA provider") + @Param(description = "The host HA provider") private String provider; @SerializedName(ApiConstants.STATUS) - @Param(description = "operation status") + @Param(description = "Operation status") private Boolean status; public HostHAResponse() { 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 ca31bd8b1556..10bd62804fb2 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 @@ -36,133 +36,137 @@ @EntityReference(value = Host.class) public class HostResponse extends BaseResponseWithAnnotations { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the host") + @Param(description = "The ID of the host") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the host") + @Param(description = "The name of the host") private String name; @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the host") + @Param(description = "The state of the host") private Status state; @SerializedName("disconnected") - @Param(description = "true if the host is disconnected. False otherwise.") + @Param(description = "True if the host is disconnected. False otherwise.") private Date disconnectedOn; @SerializedName(ApiConstants.TYPE) - @Param(description = "the host type") + @Param(description = "The host type") private Host.Type hostType; @SerializedName("oscategoryid") - @Param(description = "the OS category ID of the host") + @Param(description = "The OS category ID of the host") private String osCategoryId; @SerializedName("oscategoryname") - @Param(description = "the OS category name of the host") + @Param(description = "The OS category name of the host") private String osCategoryName; + @SerializedName(ApiConstants.GUEST_OS_RULE) + @Param(description = "the guest OS rule") + private String guestOsRule; + @SerializedName(ApiConstants.IP_ADDRESS) - @Param(description = "the IP address of the host") + @Param(description = "The IP address of the host") private String ipAddress; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the Zone ID of the host") + @Param(description = "The Zone ID of the host") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "the Zone name of the host") + @Param(description = "The Zone name of the host") private String zoneName; @SerializedName(ApiConstants.POD_ID) - @Param(description = "the Pod ID of the host") + @Param(description = "The Pod ID of the host") private String podId; @SerializedName("podname") - @Param(description = "the Pod name of the host") + @Param(description = "The Pod name of the host") private String podName; @SerializedName("version") - @Param(description = "the host version") + @Param(description = "The host version") private String version; @SerializedName(ApiConstants.HYPERVISOR) - @Param(description = "the host hypervisor") + @Param(description = "The host hypervisor") private String hypervisor; @SerializedName("cpusockets") - @Param(description = "the number of CPU sockets on the host") + @Param(description = "The number of CPU sockets on the host") private Integer cpuSockets; @SerializedName("cpunumber") - @Param(description = "the CPU number of the host") + @Param(description = "The CPU number of the host") private Integer cpuNumber; @SerializedName("cpuspeed") - @Param(description = "the CPU speed of the host") + @Param(description = "The CPU speed of the host") private Long cpuSpeed; @Deprecated @SerializedName("cpuallocated") - @Param(description = "the amount of the host's CPU currently allocated") + @Param(description = "The amount of the host's CPU currently allocated") private String cpuAllocated; @SerializedName("cpuallocatedvalue") - @Param(description = "the amount of the host's CPU currently allocated in MHz") + @Param(description = "The amount of the host's CPU currently allocated in MHz") private Long cpuAllocatedValue; @SerializedName("cpuallocatedpercentage") - @Param(description = "the amount of the host's CPU currently allocated in percentage") + @Param(description = "The amount of the host's CPU currently allocated in percentage") private String cpuAllocatedPercentage; @SerializedName("cpuallocatedwithoverprovisioning") - @Param(description = "the amount of the host's CPU currently allocated after applying the cpu.overprovisioning.factor") + @Param(description = "The amount of the host's CPU currently allocated after applying the cpu.overprovisioning.factor") private String cpuAllocatedWithOverprovisioning; @SerializedName("cpuused") - @Param(description = "the amount of the host's CPU currently used") + @Param(description = "The amount of the host's CPU currently used") private String cpuUsed; @SerializedName("cpuwithoverprovisioning") - @Param(description = "the amount of the host's CPU after applying the cpu.overprovisioning.factor") + @Param(description = "The amount of the host's CPU after applying the cpu.overprovisioning.factor") private String cpuWithOverprovisioning; @SerializedName(ApiConstants.CPU_LOAD_AVERAGE) - @Param(description = "the cpu average load on the host") + @Param(description = "The average CPU load on the host") private Double cpuloadaverage; @SerializedName("networkkbsread") - @Param(description = "the incoming network traffic on the host") + @Param(description = "The incoming Network traffic on the host") private Long networkKbsRead; @SerializedName("networkkbswrite") - @Param(description = "the outgoing network traffic on the host") + @Param(description = "The outgoing Network traffic on the host") private Long networkKbsWrite; @Deprecated @SerializedName("memorytotal") - @Param(description = "the memory total of the host, this parameter is deprecated use memorywithoverprovisioning") + @Param(description = "The memory total of the host, this parameter is deprecated use memorywithoverprovisioning") private Long memoryTotal; @SerializedName("memorywithoverprovisioning") - @Param(description = "the amount of the host's memory after applying the mem.overprovisioning.factor") + @Param(description = "The amount of the host's memory after applying the mem.overprovisioning.factor") private String memWithOverprovisioning; @Deprecated @SerializedName("memoryallocated") - @Param(description = "the amount of the host's memory currently allocated") + @Param(description = "The amount of the host's memory currently allocated") private long memoryAllocated; @SerializedName("memoryallocatedpercentage") - @Param(description = "the amount of the host's memory currently allocated in percentage") + @Param(description = "The amount of the host's memory currently allocated in percentage") private String memoryAllocatedPercentage; @SerializedName("memoryallocatedbytes") - @Param(description = "the amount of the host's memory currently allocated in bytes") + @Param(description = "The amount of the host's memory currently allocated in bytes") private Long memoryAllocatedBytes; @SerializedName("memoryused") - @Param(description = "the amount of the host's memory currently used") + @Param(description = "The amount of the host's memory currently used") private Long memoryUsed; @SerializedName("gputotal") @@ -178,27 +182,31 @@ public class HostResponse extends BaseResponseWithAnnotations { private List gpuGroup; @SerializedName("disksizetotal") - @Param(description = "the total disk size of the host") + @Param(description = "The total disk size of the host") private Long diskSizeTotal; @SerializedName("disksizeallocated") - @Param(description = "the host's currently allocated disk size") + @Param(description = "The host's currently allocated disk size") private Long diskSizeAllocated; @SerializedName("capabilities") - @Param(description = "capabilities of the host") + @Param(description = "Capabilities of the host") private String capabilities; @SerializedName("lastpinged") - @Param(description = "the date and time the host was last pinged") + @Param(description = "The date and time the host was last pinged") private Date lastPinged; @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) @Param(description = "the virtual machine id for host type ConsoleProxy and SecondaryStorageVM", since = "4.21.0") private String virtualMachineId; + @SerializedName("msid") + @Param(description = "(only for details=core) the msid of the host's management server") + private Long msId; + @SerializedName(ApiConstants.MANAGEMENT_SERVER_ID) - @Param(description = "the management server ID of the host") + @Param(description = "The management server ID of the host") private String managementServerId; @SerializedName(ApiConstants.MANAGEMENT_SERVER_NAME) @@ -208,35 +216,35 @@ public class HostResponse extends BaseResponseWithAnnotations { private transient long clusterInternalId; @SerializedName("clusterid") - @Param(description = "the cluster ID of the host") + @Param(description = "The cluster ID of the host") private String clusterId; @SerializedName("clustername") - @Param(description = "the cluster name of the host") + @Param(description = "The cluster name of the host") private String clusterName; @SerializedName("clustertype") - @Param(description = "the cluster type of the cluster that host belongs to") + @Param(description = "The cluster type of the cluster that host belongs to") private String clusterType; @SerializedName("islocalstorageactive") - @Param(description = "true if local storage is active, false otherwise") + @Param(description = "True if local storage is active, false otherwise") private Boolean localStorageActive; @SerializedName(ApiConstants.CREATED) - @Param(description = "the date and time the host was created") + @Param(description = "The date and time the host was created") private Date created; @SerializedName("removed") - @Param(description = "the date and time the host was removed") + @Param(description = "The date and time the host was removed") private Date removed; @SerializedName("events") - @Param(description = "events available for the host") + @Param(description = "Events available for the host") private String events; @SerializedName("hosttags") - @Param(description = "comma-separated list of tags for the host") + @Param(description = "Comma-separated list of tags for the host") private String hostTags; @SerializedName("explicithosttags") @@ -252,31 +260,31 @@ public class HostResponse extends BaseResponseWithAnnotations { private Boolean isTagARule; @SerializedName("hasenoughcapacity") - @Param(description = "true if this host has enough CPU and RAM capacity to migrate a VM to it, false otherwise") + @Param(description = "True if this host has enough CPU and RAM capacity to migrate an Instance to it, false otherwise") private Boolean hasEnoughCapacity; @SerializedName("suitableformigration") - @Param(description = "true if this host is suitable(has enough capacity and satisfies all conditions like hosttags, max guests vm limit etc) to migrate a VM to it , false otherwise") + @Param(description = "True if this host is suitable(has enough capacity and satisfies all conditions like hosttags, max guests Instance limit etc) to migrate an Instance to it , false otherwise") private Boolean suitableForMigration; @SerializedName("hostha") - @Param(description = "the host HA information information") + @Param(description = "The host HA information information") private HostHAResponse hostHAResponse; @SerializedName("outofbandmanagement") - @Param(description = "the host out-of-band management information") + @Param(description = "The host out-of-band management information") private OutOfBandManagementResponse outOfBandManagementResponse; @SerializedName("resourcestate") - @Param(description = "the resource state of the host") + @Param(description = "The resource state of the host") private String resourceState; @SerializedName(ApiConstants.HYPERVISOR_VERSION) - @Param(description = "the hypervisor version") + @Param(description = "The hypervisor version") private String hypervisorVersion; @SerializedName(ApiConstants.HA_HOST) - @Param(description = "true if the host is Ha host (dedicated to vms started by HA process; false otherwise") + @Param(description = "True if the host is Ha host (dedicated to Instances started by HA process; false otherwise") private Boolean haHost; @SerializedName(ApiConstants.DETAILS) @@ -284,23 +292,23 @@ public class HostResponse extends BaseResponseWithAnnotations { private Map details; @SerializedName(ApiConstants.ANNOTATION) - @Param(description = "the last annotation set on this host by an admin", since = "4.11") + @Param(description = "The last annotation set on this host by an admin", since = "4.11") private String annotation; @SerializedName(ApiConstants.LAST_ANNOTATED) - @Param(description = "the last time this host was annotated", since = "4.11") + @Param(description = "The last time this host was annotated", since = "4.11") private Date lastAnnotated; @SerializedName(ApiConstants.USERNAME) - @Param(description = "the admin that annotated this host", since = "4.11") + @Param(description = "The admin that annotated this host", since = "4.11") private String username; @SerializedName("ueficapability") - @Param(description = "true if the host has capability to support UEFI boot") + @Param(description = "True if the host has capability to support UEFI boot") private Boolean uefiCapability; @SerializedName(ApiConstants.ENCRYPTION_SUPPORTED) - @Param(description = "true if the host supports encryption", since = "4.18") + @Param(description = "True if the host supports encryption", since = "4.18") private Boolean encryptionSupported; @SerializedName(ApiConstants.INSTANCE_CONVERSION_SUPPORTED) @@ -488,6 +496,14 @@ public void setVirtualMachineId(String virtualMachineId) { this.virtualMachineId = virtualMachineId; } + public Long getMsId() { + return msId; + } + + public void setMsId(Long msId) { + this.msId = msId; + } + public void setManagementServerId(String managementServerId) { this.managementServerId = managementServerId; } @@ -999,4 +1015,12 @@ public void setExtensionName(String extensionName) { public String getExtensionName() { return extensionName; } + + public String getGuestOsRule() { + return guestOsRule; + } + + public void setGuestOsRule(String guestOsRule) { + this.guestOsRule = guestOsRule; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/HostTagResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HostTagResponse.java index f772da6dcb66..fac30cae8c03 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/HostTagResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/HostTagResponse.java @@ -24,15 +24,15 @@ public class HostTagResponse extends BaseResponse { @SerializedName("id") - @Param(description = "the ID of the host tag") + @Param(description = "The ID of the host tag") private String id; @SerializedName("hostid") - @Param(description = "the host ID of the host tag") + @Param(description = "The host ID of the host tag") private long hostId; @SerializedName("name") - @Param(description = "the name of the host tag") + @Param(description = "The name of the host tag") private String name; @SerializedName(ApiConstants.IS_IMPLICIT) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/HypervisorCapabilitiesResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HypervisorCapabilitiesResponse.java index c19397e0c835..a835b2376272 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/HypervisorCapabilitiesResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/HypervisorCapabilitiesResponse.java @@ -27,39 +27,39 @@ @EntityReference(value = HypervisorCapabilities.class) public class HypervisorCapabilitiesResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the hypervisor capabilities row") + @Param(description = "The hypervisor capability ID") private String id; @SerializedName(ApiConstants.HYPERVISOR_VERSION) - @Param(description = "the hypervisor version") + @Param(description = "The hypervisor version") private String hypervisorVersion; @SerializedName(ApiConstants.HYPERVISOR) - @Param(description = "the hypervisor type") + @Param(description = "The hypervisor type") private String hypervisor; @SerializedName(ApiConstants.MAX_GUESTS_LIMIT) - @Param(description = "the maximum number of guest vms recommended for this hypervisor") + @Param(description = "The maximum number of guest Instances recommended for this hypervisor") private Long maxGuestsLimit; @SerializedName(ApiConstants.SECURITY_GROUP_EANBLED) - @Param(description = "true if security group is supported") + @Param(description = "True if security group is supported") private boolean isSecurityGroupEnabled; @SerializedName(ApiConstants.MAX_DATA_VOLUMES_LIMIT) - @Param(description = "the maximum number of Data Volumes that can be attached for this hypervisor") + @Param(description = "The maximum number of Data Volumes that can be attached for this hypervisor") private Integer maxDataVolumesLimit; @SerializedName(ApiConstants.MAX_HOSTS_PER_CLUSTER) - @Param(description = "the maximum number of Hosts per cluster for this hypervisor") + @Param(description = "The maximum number of Hosts per cluster for this hypervisor") private Integer maxHostsPerCluster; @SerializedName(ApiConstants.STORAGE_MOTION_ENABLED) - @Param(description = "true if storage motion is supported") + @Param(description = "True if storage motion is supported") private boolean isStorageMotionSupported; @SerializedName(ApiConstants.VM_SNAPSHOT_ENABELD) - @Param(description = "true if VM snapshots are enabled for this hypervisor") + @Param(description = "True if Instance Snapshots are enabled for this hypervisor") private boolean isVmSnapshotEnabled; public HypervisorCapabilitiesResponse(){ diff --git a/api/src/main/java/org/apache/cloudstack/api/response/HypervisorGuestOsNamesResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HypervisorGuestOsNamesResponse.java index 52f55bf45693..e6441690083d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/HypervisorGuestOsNamesResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/HypervisorGuestOsNamesResponse.java @@ -27,19 +27,19 @@ public class HypervisorGuestOsNamesResponse extends BaseResponse { @SerializedName(ApiConstants.HYPERVISOR) - @Param(description = "the hypervisor") + @Param(description = "The hypervisor") private String hypervisor; @SerializedName(ApiConstants.HYPERVISOR_VERSION) - @Param(description = "version of the hypervisor for guest os names") + @Param(description = "Version of the hypervisor for guest os names") private String hypervisorVersion; @SerializedName(ApiConstants.GUEST_OS_LIST) - @Param(description = "the guest OS list of the hypervisor", responseObject = HypervisorGuestOsResponse.class) + @Param(description = "The guest OS list of the hypervisor", responseObject = HypervisorGuestOsResponse.class) private List guestOSList; @SerializedName(ApiConstants.GUEST_OS_COUNT) - @Param(description = "the guest OS count of the hypervisor") + @Param(description = "The guest OS count of the hypervisor") private Integer guestOSCount; public String getHypervisor() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/HypervisorGuestOsResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HypervisorGuestOsResponse.java index e9ef630e17fc..53da01a98549 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/HypervisorGuestOsResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/HypervisorGuestOsResponse.java @@ -26,11 +26,11 @@ public class HypervisorGuestOsResponse extends BaseResponse { @SerializedName(ApiConstants.OS_DISPLAY_NAME) - @Param(description = "standard display name for the Guest OS") + @Param(description = "Standard display name for the Guest OS") private String osStdName; @SerializedName(ApiConstants.OS_NAME_FOR_HYPERVISOR) - @Param(description = "hypervisor specific name for the Guest OS") + @Param(description = "Hypervisor specific name for the Guest OS") private String osNameForHypervisor; public String getOsStdName() { 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 8b0e3cc02016..cdc3115c8293 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 @@ -32,47 +32,47 @@ @SuppressWarnings("unused") public class IPAddressResponse extends BaseResponseWithAnnotations implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "public IP address id") + @Param(description = "Public IP address ID") private String id; @SerializedName(ApiConstants.IP_ADDRESS) - @Param(description = "public IP address") + @Param(description = "Public IP address") private String ipAddress; @SerializedName("allocated") - @Param(description = "date the public IP address was acquired") + @Param(description = "Date the public IP address was acquired") private Date allocated; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the ID of the zone the public IP address belongs to") + @Param(description = "The ID of the zone the public IP address belongs to") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "the name of the zone the public IP address belongs to") + @Param(description = "The name of the zone the public IP address belongs to") private String zoneName; @SerializedName("issourcenat") - @Param(description = "true if the IP address is a source nat address, false otherwise") + @Param(description = "True if the IP address is a source NAT address, false otherwise") private Boolean sourceNat; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account the public IP address is associated with") + @Param(description = "The Account the public IP address is associated with") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the ipaddress") + @Param(description = "The project id of the IP address") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the address") + @Param(description = "The project name of the address") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID the public IP address is associated with") + @Param(description = "The domain ID the public IP address is associated with") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain the public IP address is associated with") + @Param(description = "The domain the public IP address is associated with") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) @@ -80,95 +80,95 @@ public class IPAddressResponse extends BaseResponseWithAnnotations implements Co private String domainPath; @SerializedName(ApiConstants.FOR_VIRTUAL_NETWORK) - @Param(description = "the virtual network for the IP address") + @Param(description = "The virtual Network for the IP address") private Boolean forVirtualNetwork; @SerializedName(ApiConstants.VLAN_ID) - @Param(description = "the ID of the VLAN associated with the IP address." + " This parameter is visible to ROOT admins only") + @Param(description = "The ID of the VLAN associated with the IP address." + " This parameter is visible to ROOT admins only") private String vlanId; @SerializedName("vlanname") - @Param(description = "the VLAN associated with the IP address") + @Param(description = "The VLAN associated with the IP address") private String vlanName; @SerializedName("isstaticnat") - @Param(description = "true if this ip is for static nat, false otherwise") + @Param(description = "True if this IP is for static NAT, false otherwise") private Boolean staticNat; @SerializedName(ApiConstants.IS_SYSTEM) - @Param(description = "true if this ip is system ip (was allocated as a part of deployVm or createLbRule)") + @Param(description = "True if this IP is system IP (was allocated as a part of deployVm or createLbRule)") private Boolean isSystem; @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) - @Param(description = "virtual machine id the ip address is assigned to") + @Param(description = "Instance id the IP address is assigned to") private String virtualMachineId; @SerializedName(ApiConstants.VIRTUAL_MACHINE_TYPE) - @Param(description = "virtual machine type the ip address is assigned to", since = "4.19.0") + @Param(description = "Instance type the IP address is assigned to", since = "4.19.0") private String virtualMachineType; @SerializedName("vmipaddress") - @Param(description = "virtual machine (dnat) ip address (not null only for static nat Ip)") + @Param(description = "Instance (DNAT) IP address (not null only for static NAT IP)") private String virtualMachineIp; @SerializedName("virtualmachinename") - @Param(description = "virtual machine name the ip address is assigned to") + @Param(description = "Instance name the IP address is assigned to") private String virtualMachineName; @SerializedName("virtualmachinedisplayname") - @Param(description = "virtual machine display name the ip address is assigned to (not null only for static nat Ip)") + @Param(description = "Instance display name the IP address is assigned to (not null only for static NAT IP)") private String virtualMachineDisplayName; @SerializedName(ApiConstants.ASSOCIATED_NETWORK_ID) - @Param(description = "the ID of the Network associated with the IP address") + @Param(description = "The ID of the Network associated with the IP address") private String associatedNetworkId; @SerializedName(ApiConstants.ASSOCIATED_NETWORK_NAME) - @Param(description = "the name of the Network associated with the IP address") + @Param(description = "The name of the Network associated with the IP address") private String associatedNetworkName; @SerializedName(ApiConstants.NETWORK_ID) - @Param(description = "the ID of the Network where ip belongs to") + @Param(description = "The ID of the Network where IP belongs to") private String networkId; @SerializedName(ApiConstants.STATE) - @Param(description = "State of the ip address. Can be: Allocating, Allocated, Releasing, Reserved and Free") + @Param(description = "State of the IP address. Can be: Allocating, Allocated, Releasing, Reserved and Free") private String state; @SerializedName(ApiConstants.PHYSICAL_NETWORK_ID) - @Param(description = "the physical network this belongs to") + @Param(description = "The physical Network this belongs to") private String physicalNetworkId; @SerializedName(ApiConstants.PURPOSE) - @Param(description = "purpose of the IP address. In Acton this value is not null for Ips with isSystem=true, and can have either StaticNat or LB value") + @Param(description = "Purpose of the IP address. In Acton this value is not null for IPs with isSystem=true, and can have either StaticNat or LB value") private String purpose; @SerializedName(ApiConstants.VPC_ID) - @Param(description = "VPC id the ip belongs to") + @Param(description = "VPC ID the IP belongs to") private String vpcId; @SerializedName(ApiConstants.VPC_NAME) - @Param(description = "VPC name the ip belongs to", since = "4.13.2") + @Param(description = "VPC name the IP belongs to", since = "4.13.2") private String vpcName; @SerializedName(ApiConstants.TAGS) - @Param(description = "the list of resource tags associated with ip address", responseObject = ResourceTagResponse.class) + @Param(description = "The list of resource tags associated with IP address", responseObject = ResourceTagResponse.class) private List tags; @SerializedName(ApiConstants.IS_PORTABLE) - @Param(description = "is public IP portable across the zones") + @Param(description = "Is public IP portable across the zones") private Boolean isPortable; @SerializedName(ApiConstants.FOR_DISPLAY) - @Param(description = "is public ip for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) + @Param(description = "Is public IP for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; @SerializedName(ApiConstants.NETWORK_NAME) - @Param(description="the name of the Network where ip belongs to") + @Param(description = "The name of the Network where IP belongs to") private String networkName; @SerializedName(ApiConstants.HAS_RULES) - @Param(description="whether the ip address has Firewall/PortForwarding/LoadBalancing rules defined") + @Param(description = "Whether the IP address has Firewall/PortForwarding/LoadBalancing rules defined") private boolean hasRules; @SerializedName(ApiConstants.FOR_SYSTEM_VMS) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ImageStoreDetailResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ImageStoreDetailResponse.java index 8863b632681d..0afef6166f8b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ImageStoreDetailResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ImageStoreDetailResponse.java @@ -24,11 +24,11 @@ public class ImageStoreDetailResponse extends BaseResponse { @SerializedName("name") - @Param(description = "detail property name of the image store") + @Param(description = "Detail property name of the image store") private String name; @SerializedName("value") - @Param(description = "detail property value of the image store") + @Param(description = "Detail property value of the image store") private String value; public ImageStoreDetailResponse() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ImageStoreResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ImageStoreResponse.java index ee44b6bc4745..79f7eb295ea2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ImageStoreResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ImageStoreResponse.java @@ -28,47 +28,47 @@ @EntityReference(value = ImageStore.class) public class ImageStoreResponse extends BaseResponseWithAnnotations { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the image store") + @Param(description = "The ID of the image store") private String id; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the Zone ID of the image store") + @Param(description = "The Zone ID of the image store") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "the Zone name of the image store") + @Param(description = "The Zone name of the image store") private String zoneName; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the image store") + @Param(description = "The name of the image store") private String name; @SerializedName(ApiConstants.URL) - @Param(description = "the url of the image store") + @Param(description = "The URL of the image store") private String url; @SerializedName(ApiConstants.PROTOCOL) - @Param(description = "the protocol of the image store") + @Param(description = "The protocol of the image store") private String protocol; @SerializedName("providername") - @Param(description = "the provider name of the image store") + @Param(description = "The provider name of the image store") private String providerName; @SerializedName(ApiConstants.SCOPE) - @Param(description = "the scope of the image store") + @Param(description = "The scope of the image store") private ScopeType scope; @SerializedName(ApiConstants.READ_ONLY) - @Param(description = "defines if store is read-only") + @Param(description = "Defines if store is read-only") private Boolean readonly; @SerializedName("disksizetotal") - @Param(description = "the total disk size of the host") + @Param(description = "The total disk size of the host") private Long diskSizeTotal; @SerializedName("disksizeused") - @Param(description = "the host's currently used disk size") + @Param(description = "The host's currently used disk size") private Long diskSizeUsed; public ImageStoreResponse() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ImageTransferResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ImageTransferResponse.java new file mode 100644 index 000000000000..15576e8f1012 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ImageTransferResponse.java @@ -0,0 +1,104 @@ +//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.api.response; + +import java.util.Date; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.backup.ImageTransfer; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = ImageTransfer.class) +public class ImageTransferResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "the ID of the image transfer") + private String id; + + @SerializedName("backupid") + @Param(description = "the backup ID") + private String backupId; + + @SerializedName("vmid") + @Param(description = "the VM ID") + private String vmId; + + @SerializedName(ApiConstants.VOLUME_ID) + @Param(description = "the disk/volume ID") + private String diskId; + + @SerializedName("devicename") + @Param(description = "the device name (vda, vdb, etc)") + private String deviceName; + + @SerializedName("transferurl") + @Param(description = "the transfer URL") + private String transferUrl; + + @SerializedName("phase") + @Param(description = "the transfer phase") + private String phase; + + @SerializedName("direction") + @Param(description = "the image transfer direction: upload / download") + private String direction; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "the date created") + private Date created; + + public void setId(String id) { + this.id = id; + } + + public void setBackupId(String backupId) { + this.backupId = backupId; + } + + public void setVmId(String vmId) { + this.vmId = vmId; + } + + public void setDiskId(String diskId) { + this.diskId = diskId; + } + + public void setDeviceName(String deviceName) { + this.deviceName = deviceName; + } + + public void setTransferUrl(String transferUrl) { + this.transferUrl = transferUrl; + } + + public void setPhase(String phase) { + this.phase = phase; + } + + public void setDirection(String direction) { + this.direction = direction; + } + + public void setCreated(Date created) { + this.created = created; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ImportVMTaskResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ImportVMTaskResponse.java new file mode 100644 index 000000000000..aa85554f5678 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ImportVMTaskResponse.java @@ -0,0 +1,257 @@ +// +// 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 java.util.Date; + +public class ImportVMTaskResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "the ID of importing task") + private String id; + + @SerializedName(ApiConstants.ZONE_ID) + @Param(description = "the Zone ID") + private String zoneId; + + @SerializedName(ApiConstants.ZONE_NAME) + @Param(description = "the Zone name") + private String zoneName; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "the account name") + private String accountName; + + @SerializedName(ApiConstants.ACCOUNT_ID) + @Param(description = "the ID of account") + private String accountId; + + @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) + @Param(description = "the ID of the imported VM (after task is completed)") + private String virtualMachineId; + + @SerializedName(ApiConstants.DISPLAY_NAME) + @Param(description = "the display name of the importing VM") + private String displayName; + + @SerializedName(ApiConstants.STATE) + @Param(description = "the state of the importing VM task") + private String state; + + @SerializedName(ApiConstants.VCENTER) + @Param(description = "the vcenter name of the importing VM task") + private String vcenter; + + @SerializedName(ApiConstants.DATACENTER_NAME) + @Param(description = "the datacenter name of the importing VM task") + private String datacenterName; + + @SerializedName("sourcevmname") + @Param(description = "the source VM name") + private String sourceVMName; + + @SerializedName("step") + @Param(description = "the current step on the importing VM task") + private String step; + + @SerializedName("stepduration") + @Param(description = "the duration of the current step") + private String stepDuration; + + @SerializedName(ApiConstants.DURATION) + @Param(description = "the total task duration") + private String duration; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "the current step description on the importing VM task") + private String description; + + @SerializedName(ApiConstants.CONVERT_INSTANCE_HOST_ID) + @Param(description = "the ID of the host on which the instance is being converted") + private String convertInstanceHostId; + + @SerializedName("convertinstancehostname") + @Param(description = "the name of the host on which the instance is being converted") + private String convertInstanceHostName; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "the create date of the importing task") + private Date created; + + @SerializedName(ApiConstants.LAST_UPDATED) + @Param(description = "the last updated date of the importing task") + private Date lastUpdated; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getZoneId() { + return zoneId; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } + + public String getZoneName() { + return zoneName; + } + + public void setZoneName(String zoneName) { + this.zoneName = zoneName; + } + + public String getAccountName() { + return accountName; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public String getAccountId() { + return accountId; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } + + public String getVirtualMachineId() { + return virtualMachineId; + } + + public void setVirtualMachineId(String virtualMachineId) { + this.virtualMachineId = virtualMachineId; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public String getVcenter() { + return vcenter; + } + + public void setVcenter(String vcenter) { + this.vcenter = vcenter; + } + + public String getDatacenterName() { + return datacenterName; + } + + public void setDatacenterName(String datacenterName) { + this.datacenterName = datacenterName; + } + + public String getSourceVMName() { + return sourceVMName; + } + + public void setSourceVMName(String sourceVMName) { + this.sourceVMName = sourceVMName; + } + + public String getStep() { + return step; + } + + public void setStep(String step) { + this.step = step; + } + + public String getStepDuration() { + return stepDuration; + } + + public void setStepDuration(String stepDuration) { + this.stepDuration = stepDuration; + } + + public String getDuration() { + return duration; + } + + public void setDuration(String duration) { + this.duration = duration; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getConvertInstanceHostId() { + return convertInstanceHostId; + } + + public void setConvertInstanceHostId(String convertInstanceHostId) { + this.convertInstanceHostId = convertInstanceHostId; + } + + public String getConvertInstanceHostName() { + return convertInstanceHostName; + } + + public void setConvertInstanceHostName(String convertInstanceHostName) { + this.convertInstanceHostName = convertInstanceHostName; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public Date getLastUpdated() { + return lastUpdated; + } + + public void setLastUpdated(Date lastUpdated) { + this.lastUpdated = lastUpdated; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/InstanceGroupResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/InstanceGroupResponse.java index 9c7a4fc09a17..f15ccf611965 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/InstanceGroupResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/InstanceGroupResponse.java @@ -32,35 +32,35 @@ public class InstanceGroupResponse extends BaseResponseWithAnnotations implements ControlledViewEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the instance group") + @Param(description = "The ID of the Instance group") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the instance group") + @Param(description = "The name of the Instance group") private String name; @SerializedName(ApiConstants.CREATED) - @Param(description = "time and date the instance group was created") + @Param(description = "Time and date the Instance group was created") private Date created; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account owning the instance group") + @Param(description = "The Account owning the Instance group") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project ID of the instance group") + @Param(description = "The project ID of the Instance group") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the instance group") + @Param(description = "The project name of the Instance group") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID of the instance group") + @Param(description = "The domain ID of the Instance group") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name of the instance group") + @Param(description = "The domain name of the Instance group") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/InternalLoadBalancerElementResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/InternalLoadBalancerElementResponse.java index 15b17d88851c..59726e1c3fbf 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/InternalLoadBalancerElementResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/InternalLoadBalancerElementResponse.java @@ -29,11 +29,11 @@ @SuppressWarnings("unused") public class InternalLoadBalancerElementResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the id of the internal load balancer element") + @Param(description = "The ID of the internal Load balancer element") private String id; @SerializedName(ApiConstants.NSP_ID) - @Param(description = "the physical network service provider id of the element") + @Param(description = "The physical Network service provider ID of the element") private String nspId; @SerializedName(ApiConstants.ENABLED) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/IpForwardingRuleResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/IpForwardingRuleResponse.java index e9856c7af1a7..ba3a0ae3edb8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/IpForwardingRuleResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/IpForwardingRuleResponse.java @@ -25,43 +25,43 @@ public class IpForwardingRuleResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the port forwarding rule") + @Param(description = "The ID of the port forwarding rule") private String id; @SerializedName(ApiConstants.PROTOCOL) - @Param(description = "the protocol of the port forwarding rule") + @Param(description = "The protocol of the port forwarding rule") private String protocol; @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) - @Param(description = "the VM ID for the port forwarding rule") + @Param(description = "The Instance ID for the port forwarding rule") private String virtualMachineId; @SerializedName("virtualmachinename") - @Param(description = "the VM name for the port forwarding rule") + @Param(description = "The Instance name for the port forwarding rule") private String virtualMachineName; @SerializedName("virtualmachinedisplayname") - @Param(description = "the VM display name for the port forwarding rule") + @Param(description = "The Instance display name for the port forwarding rule") private String virtualMachineDisplayName; @SerializedName(ApiConstants.IP_ADDRESS_ID) - @Param(description = "the public ip address id for the port forwarding rule") + @Param(description = "The public IP address id for the port forwarding rule") private Long publicIpAddressId; @SerializedName(ApiConstants.IP_ADDRESS) - @Param(description = "the public ip address for the port forwarding rule") + @Param(description = "The public IP address for the port forwarding rule") private String publicIpAddress; @SerializedName(ApiConstants.START_PORT) - @Param(description = "the start port of the rule") + @Param(description = "The start port of the rule") private Integer startPort; @SerializedName(ApiConstants.END_PORT) - @Param(description = "the end port of the rule") + @Param(description = "The end port of the rule") private Integer endPort; @SerializedName(ApiConstants.STATE) - @Param(description = "state of the ip forwarding rule") + @Param(description = "State of the IP forwarding rule") private String state; public String getId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/IpRangeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/IpRangeResponse.java index 364c19f2df0b..5c93287ad07a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/IpRangeResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/IpRangeResponse.java @@ -26,27 +26,27 @@ public class IpRangeResponse extends BaseResponse { @SerializedName(ApiConstants.GATEWAY) - @Param(description = "the gateway for the range") + @Param(description = "The gateway for the range") private String gateway; @SerializedName(ApiConstants.CIDR) - @Param(description = "the CIDR for the range") + @Param(description = "The CIDR for the range") private String cidr; @SerializedName(ApiConstants.START_IP) - @Param(description = "the starting IP for the range") + @Param(description = "The starting IP for the range") private String startIp; @SerializedName(ApiConstants.END_IP) - @Param(description = "the ending IP for the range") + @Param(description = "The ending IP for the range") private String endIp; @SerializedName(ApiConstants.FOR_SYSTEM_VMS) - @Param(description = "indicates if range is dedicated for CPVM and SSVM") + @Param(description = "Indicates if range is dedicated for CPVM and SSVM") private String forSystemVms; @SerializedName(ApiConstants.VLAN_ID) - @Param(description = "indicates Vlan ID for the range") + @Param(description = "Indicates VLAN ID for the range") private String vlanId; public String getGateway() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/Ipv6RouteResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/Ipv6RouteResponse.java index 4111ed94d5b8..68f05cdd6ce9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/Ipv6RouteResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/Ipv6RouteResponse.java @@ -26,11 +26,11 @@ public class Ipv6RouteResponse extends BaseResponse { @SerializedName(ApiConstants.SUBNET) - @Param(description = "the guest IPv6 cidr for route") + @Param(description = "The guest IPv6 CIDR for route") private String subnet; @SerializedName(ApiConstants.GATEWAY) - @Param(description = "the outbound IPv6 gateway") + @Param(description = "The outbound IPv6 gateway") private String gateway; public Ipv6RouteResponse(String subnet, String gateway) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/IsAccountAllowedToCreateOfferingsWithTagsResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/IsAccountAllowedToCreateOfferingsWithTagsResponse.java index 5c763c2058a7..ed7f3b57a9d1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/IsAccountAllowedToCreateOfferingsWithTagsResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/IsAccountAllowedToCreateOfferingsWithTagsResponse.java @@ -23,7 +23,7 @@ public class IsAccountAllowedToCreateOfferingsWithTagsResponse extends BaseResponse { @SerializedName("isallowed") - @Param(description = "is domain admin allowed to create offerings with tags") + @Param(description = "Is domain admin allowed to create offerings with tags") private Boolean isAllowed; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/IsoVmResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/IsoVmResponse.java index 27f286dfca5d..1b4b90d4dc4b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/IsoVmResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/IsoVmResponse.java @@ -28,47 +28,47 @@ @SuppressWarnings("unused") public class IsoVmResponse extends BaseResponse { @SerializedName("id") - @Param(description = "the ISO ID") + @Param(description = "The ISO ID") private String id; @SerializedName("name") - @Param(description = "the ISO name") + @Param(description = "The ISO name") private String name; @SerializedName("displaytext") - @Param(description = "the ISO display text") + @Param(description = "The ISO display text") private String displayText; @SerializedName("bootable") - @Param(description = "true if the ISO is bootable, false otherwise") + @Param(description = "True if the ISO is bootable, false otherwise") private Boolean bootable; @SerializedName("isfeatured") - @Param(description = "true if this template is a featured template, false otherwise") + @Param(description = "True if this Template is a featured Template, false otherwise") private Boolean featured; @SerializedName("ostypeid") - @Param(description = "the ID of the OS type for this template.") + @Param(description = "The ID of the OS type for this Template.") private String osTypeId; @SerializedName("ostypename") - @Param(description = "the name of the OS type for this template.") + @Param(description = "The name of the OS type for this Template.") private String osTypeName; @SerializedName("virtualmachineid") - @Param(description = "id of the virtual machine") + @Param(description = "ID of the Instance") private String virtualMachineId; @SerializedName("vmname") - @Param(description = "name of the virtual machine") + @Param(description = "Name of the Instance") private String virtualMachineName; @SerializedName("vmdisplayname") - @Param(description = "display name of the virtual machine") + @Param(description = "Display name of the Instance") private String virtualMachineDisplayName; @SerializedName("vmstate") - @Param(description = "state of the virtual machine") + @Param(description = "State of the Instance") private String virtualMachineState; @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/response/KMSKeyResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/KMSKeyResponse.java new file mode 100644 index 000000000000..cd7a360a497c --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/KMSKeyResponse.java @@ -0,0 +1,272 @@ +/* + * 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.acl.RoleType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.kms.KMSKey; + +import java.util.Date; + +@EntityReference(value = KMSKey.class) +public class KMSKeyResponse extends BaseResponse implements ControlledViewEntityResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "the UUID of the key") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "the name of the key") + private String name; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "the description of the key") + private String description; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "the account owning the key") + private String accountName; + + @SerializedName(ApiConstants.ACCOUNT_ID) + @Param(description = "the account ID owning the key") + private String accountId; + + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "the domain ID of the key") + private String domainId; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "the domain name of the key") + private String domainName; + + @SerializedName(ApiConstants.DOMAIN_PATH) + @Param(description = "the domain path of the key") + private String domainPath; + + @SerializedName(ApiConstants.ZONE_ID) + @Param(description = "the zone ID where the key is valid") + private String zoneId; + + @SerializedName(ApiConstants.ZONE_NAME) + @Param(description = "the zone name where the key is valid") + private String zoneName; + + @SerializedName(ApiConstants.HSM_PROFILE_ID) + @Param(description = "the zone ID where the key is valid") + private String hsmProfileId; + + @SerializedName(ApiConstants.HSM_PROFILE) + @Param(description = "the zone name where the key is valid") + private String hsmProfileName; + + @SerializedName(ApiConstants.ALGORITHM) + @Param(description = "the encryption algorithm") + private String algorithm; + + @SerializedName(ApiConstants.KEY_BITS) + @Param(description = "the key size in bits") + private Integer keyBits; + + @SerializedName(ApiConstants.VERSION) + @Param(description = "the key size in bits") + private Integer version; + + @SerializedName(ApiConstants.ENABLED) + @Param(description = "whether the key is enabled") + private Boolean enabled; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "the creation timestamp") + private Date created; + + @SerializedName(ApiConstants.PROJECT_ID) + @Param(description = "the project ID of the key") + private String projectId; + + @SerializedName(ApiConstants.PROJECT) + @Param(description = "the project name of the key") + private String projectName; + + @SerializedName(ApiConstants.KEK_LABEL) + @Param(description = "the provider-specific KEK label (admin only)", authorized = { RoleType.Admin }) + private String kekLabel; + + 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 getAccountName() { + return accountName; + } + + @Override + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + @Override + public void setProjectId(String projectId) { + this.projectId = projectId; + } + + @Override + public void setProjectName(String projectName) { + this.projectName = projectName; + } + + public String getAccountId() { + return accountId; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } + + public String getDomainId() { + return domainId; + } + + @Override + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + public String getDomainName() { + return domainName; + } + + @Override + public void setDomainName(String domainName) { + this.domainName = domainName; + } + + public String getDomainPath() { + return domainPath; + } + + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + + public String getZoneId() { + return zoneId; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } + + public String getZoneName() { + return zoneName; + } + + public void setZoneName(String zoneName) { + this.zoneName = zoneName; + } + + public String getHsmProfileId() { + return hsmProfileId; + } + + public void setHsmProfileId(String hsmProfileId) { + this.hsmProfileId = hsmProfileId; + } + + public String getHsmProfileName() { + return hsmProfileName; + } + + public void setHsmProfileName(String hsmProfileName) { + this.hsmProfileName = hsmProfileName; + } + + public String getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(String algorithm) { + this.algorithm = algorithm; + } + + public Integer getKeyBits() { + return keyBits; + } + + public void setKeyBits(Integer keyBits) { + this.keyBits = keyBits; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public String getKekLabel() { + return kekLabel; + } + + public void setKekLabel(String kekLabel) { + this.kekLabel = kekLabel; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/LBHealthCheckPolicyResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/LBHealthCheckPolicyResponse.java index df3a7a0fb7b9..6355b03b18da 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/LBHealthCheckPolicyResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/LBHealthCheckPolicyResponse.java @@ -26,19 +26,19 @@ public class LBHealthCheckPolicyResponse extends BaseResponse { @SerializedName("id") - @Param(description = "the LB HealthCheck policy ID") + @Param(description = "The LB HealthCheck policy ID") private String id; @SerializedName("pingpath") - @Param(description = "the pingpath of the healthcheck policy") + @Param(description = "The pingpath of the healthcheck policy") private String pingpath; @SerializedName("description") - @Param(description = "the description of the healthcheck policy") + @Param(description = "The description of the healthcheck policy") private String description; @SerializedName("state") - @Param(description = "the state of the policy") + @Param(description = "The state of the policy") private String state; @SerializedName("responsetime") @@ -50,15 +50,15 @@ public class LBHealthCheckPolicyResponse extends BaseResponse { private int healthcheckInterval; @SerializedName("healthcheckthresshold") - @Param(description = "Number of consecutive health check success before declaring an instance healthy") + @Param(description = "Number of consecutive health check success before declaring an Instance healthy") private int healthcheckthresshold; @SerializedName("unhealthcheckthresshold") - @Param(description = "Number of consecutive health check failures before declaring an instance unhealthy.") + @Param(description = "Number of consecutive health check failures before declaring an Instance unhealthy.") private int unhealthcheckthresshold; @SerializedName(ApiConstants.FOR_DISPLAY) - @Param(description = "is policy for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) + @Param(description = "Is policy for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; public void setId(String id) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/LBHealthCheckResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/LBHealthCheckResponse.java index cbd953d34a30..f796a1c4d6c5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/LBHealthCheckResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/LBHealthCheckResponse.java @@ -30,27 +30,27 @@ @EntityReference(value = HealthCheckPolicy.class) public class LBHealthCheckResponse extends BaseResponse { @SerializedName("lbruleid") - @Param(description = "the LB rule ID") + @Param(description = "The LB rule ID") private String lbRuleId; @SerializedName("account") - @Param(description = "the account of the HealthCheck policy") + @Param(description = "The Account of the HealthCheck policy") private String accountName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID of the HealthCheck policy") + @Param(description = "The domain ID of the HealthCheck policy") private String domainId; @SerializedName("domain") - @Param(description = "the domain of the HealthCheck policy") + @Param(description = "The domain of the HealthCheck policy") private String domainName; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the id of the zone the HealthCheck policy belongs to") + @Param(description = "The ID of the zone the HealthCheck policy belongs to") private String zoneId; @SerializedName("healthcheckpolicy") - @Param(description = "the list of healthcheckpolicies", responseObject = LBHealthCheckPolicyResponse.class) + @Param(description = "The list of healthcheckpolicies", responseObject = LBHealthCheckPolicyResponse.class) private List healthCheckPolicies; public void setlbRuleId(String lbRuleId) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/LBStickinessPolicyResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/LBStickinessPolicyResponse.java index 82078b483622..db0a908dc72a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/LBStickinessPolicyResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/LBStickinessPolicyResponse.java @@ -32,27 +32,27 @@ public class LBStickinessPolicyResponse extends BaseResponse { @SerializedName("id") - @Param(description = "the LB Stickiness policy ID") + @Param(description = "The LB Stickiness policy ID") private String id; @SerializedName("name") - @Param(description = "the name of the Stickiness policy") + @Param(description = "The name of the Stickiness policy") private String name; @SerializedName("methodname") - @Param(description = "the method name of the Stickiness policy") + @Param(description = "The method name of the Stickiness policy") private String methodName; @SerializedName("description") - @Param(description = "the description of the Stickiness policy") + @Param(description = "The description of the Stickiness policy") private String description;; @SerializedName("state") - @Param(description = "the state of the policy") + @Param(description = "The state of the policy") private String state; @SerializedName(ApiConstants.FOR_DISPLAY) - @Param(description = "is policy for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) + @Param(description = "Is policy for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; // FIXME : if params with the same name exist more than once then values are concatenated with ":" as delimiter . @@ -60,7 +60,7 @@ public class LBStickinessPolicyResponse extends BaseResponse { // Example: {indirect=null, name=testcookie, nocache=null, domain=www.yahoo.com:www.google.com, postonly=null} // in the above there are two domains with values www.yahoo.com and www.google.com @SerializedName("params") - @Param(description = "the params of the policy") + @Param(description = "The params of the policy") private Map params; public Map getParams() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/LBStickinessResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/LBStickinessResponse.java index 75c00acfab04..7abcf307cedf 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/LBStickinessResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/LBStickinessResponse.java @@ -30,39 +30,39 @@ @EntityReference(value = StickinessPolicy.class) public class LBStickinessResponse extends BaseResponse { @SerializedName("lbruleid") - @Param(description = "the LB rule ID") + @Param(description = "The LB rule ID") private String lbRuleId; @SerializedName("name") - @Param(description = "the name of the Stickiness policy") + @Param(description = "The name of the Stickiness policy") private String name; @SerializedName("description") - @Param(description = "the description of the Stickiness policy") + @Param(description = "The description of the Stickiness policy") private String description;; @SerializedName("account") - @Param(description = "the account of the Stickiness policy") + @Param(description = "The Account of the Stickiness policy") private String accountName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID of the Stickiness policy") + @Param(description = "The domain ID of the Stickiness policy") private String domainId; @SerializedName("domain") - @Param(description = "the domain of the Stickiness policy") + @Param(description = "The domain of the Stickiness policy") private String domainName; @SerializedName("state") - @Param(description = "the state of the policy") + @Param(description = "The state of the policy") private String state; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the id of the zone the Stickiness policy belongs to") + @Param(description = "The ID of the zone the Stickiness policy belongs to") private String zoneId; @SerializedName("stickinesspolicy") - @Param(description = "the list of stickinesspolicies", responseObject = LBStickinessPolicyResponse.class) + @Param(description = "The list of stickinesspolicies", responseObject = LBStickinessPolicyResponse.class) private List stickinessPolicies; public void setlbRuleId(String lbRuleId) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/LoadBalancerResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/LoadBalancerResponse.java index e520dec09e7a..c57323d439dd 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/LoadBalancerResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/LoadBalancerResponse.java @@ -28,63 +28,63 @@ @SuppressWarnings("unused") public class LoadBalancerResponse extends BaseResponse implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the load balancer rule ID") + @Param(description = "The Load balancer rule ID") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the load balancer") + @Param(description = "The name of the Load balancer") private String name; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "the description of the load balancer") + @Param(description = "The description of the Load balancer") private String description; @SerializedName(ApiConstants.PUBLIC_IP_ID) - @Param(description = "the public ip address id") + @Param(description = "The public IP address ID") private String publicIpId; @SerializedName(ApiConstants.PUBLIC_IP) - @Param(description = "the public ip address") + @Param(description = "The public IP address") private String publicIp; @SerializedName(ApiConstants.PUBLIC_PORT) - @Param(description = "the public port") + @Param(description = "The public port") private String publicPort; @SerializedName(ApiConstants.PRIVATE_PORT) - @Param(description = "the private port") + @Param(description = "The private port") private String privatePort; @SerializedName(ApiConstants.ALGORITHM) - @Param(description = "the load balancer algorithm (source, roundrobin, leastconn)") + @Param(description = "The Load balancer algorithm (source, roundrobin, leastconn)") private String algorithm; @SerializedName(ApiConstants.NETWORK_ID) - @Param(description = "the id of the guest network the lb rule belongs to") + @Param(description = "The ID of the guest Network the LB rule belongs to") private String networkId; @SerializedName(ApiConstants.CIDR_LIST) - @Param(description = "the CIDR list to allow traffic, all other CIDRs will be blocked. Multiple entries must be separated by a single comma character (,).") + @Param(description = "The CIDR list to allow traffic, all other CIDRs will be blocked. Multiple entries must be separated by a single comma character (,).") private String cidrList; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account of the load balancer rule") + @Param(description = "The Account of the Load balancer rule") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the load balancer") + @Param(description = "The project ID of the Load balancer") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the load balancer") + @Param(description = "The project name of the Load balancer") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID of the load balancer rule") + @Param(description = "The domain ID of the Load balancer rule") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain of the load balancer rule") + @Param(description = "The domain of the Load balancer rule") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) @@ -92,27 +92,27 @@ public class LoadBalancerResponse extends BaseResponse implements ControlledEnti private String domainPath; @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the rule") + @Param(description = "The state of the rule") private String state; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the id of the zone the rule belongs to") + @Param(description = "The ID of the zone the rule belongs to") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "the name of the zone the load balancer rule belongs to", since = "4.11") + @Param(description = "The name of the zone the Load balancer rule belongs to", since = "4.11") private String zoneName; @SerializedName(ApiConstants.PROTOCOL) - @Param(description = "the protocol of the loadbalanacer rule") + @Param(description = "The protocol of the Load Balancer rule") private String lbProtocol; @SerializedName(ApiConstants.TAGS) - @Param(description = "the list of resource tags associated with load balancer", responseObject = ResourceTagResponse.class) + @Param(description = "The list of resource tags associated with Load balancer", responseObject = ResourceTagResponse.class) private List tags; @SerializedName(ApiConstants.FOR_DISPLAY) - @Param(description = "is rule for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) + @Param(description = "Is rule for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; public void setId(String id) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/LoadBalancerRuleVmMapResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/LoadBalancerRuleVmMapResponse.java index 57c45e643242..497339938bb0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/LoadBalancerRuleVmMapResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/LoadBalancerRuleVmMapResponse.java @@ -34,11 +34,11 @@ public class LoadBalancerRuleVmMapResponse extends BaseResponse { @SerializedName("loadbalancerruleinstance") - @Param(description = "the user vm set for lb rule") + @Param(description = "The user Instance set for LB rule") private UserVmResponse UserVmResponse; @SerializedName("lbvmipaddresses") - @Param(description = "IP addresses of the vm set of lb rule") + @Param(description = "IP addresses of the Instance set of LB rule") private List ipAddr; public void setIpAddr(List ipAddr) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/LoginCmdResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/LoginCmdResponse.java index 43f92db84cb5..6e3ef4678d28 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/LoginCmdResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/LoginCmdResponse.java @@ -35,31 +35,31 @@ public class LoginCmdResponse extends AuthenticationCmdResponse { private String domainId; @SerializedName(value = ApiConstants.TIMEOUT) - @Param(description = "the time period before the session has expired") + @Param(description = "The time period before the session has expired") private Integer timeout; @SerializedName(value = ApiConstants.ACCOUNT) - @Param(description = "the account name the user belongs to") + @Param(description = "The Account name the User belongs to") private String account; @SerializedName(value = ApiConstants.FIRSTNAME) - @Param(description = "first name of the user") + @Param(description = "First name of the user") private String firstName; @SerializedName(value = ApiConstants.LASTNAME) - @Param(description = "last name of the user") + @Param(description = "Last name of the user") private String lastName; @SerializedName(value = ApiConstants.TYPE) - @Param(description = "the account type (admin, domain-admin, read-only-admin, user)") + @Param(description = "The Account type (admin, domain-admin, read-only-admin, user)") private String type; @SerializedName(value = ApiConstants.TIMEZONE) - @Param(description = "user time zone") + @Param(description = "User time zone") private String timeZone; @SerializedName(value = ApiConstants.TIMEZONEOFFSET) - @Param(description = "user time zoneoffset") + @Param(description = "User time zoneoffset") private String timeZoneOffset; @SerializedName(value = ApiConstants.REGISTERED) @@ -90,6 +90,10 @@ public class LoginCmdResponse extends AuthenticationCmdResponse { @Param(description = "Management Server ID that the user logged to", since = "4.21.0.0") private String managementServerId; + @SerializedName(value = ApiConstants.PASSWORD_CHANGE_REQUIRED) + @Param(description = "Indicates whether the User is required to change password on next login.", since = "4.23.0") + private Boolean passwordChangeRequired; + public String getUsername() { return username; } @@ -223,4 +227,12 @@ public String getManagementServerId() { public void setManagementServerId(String managementServerId) { this.managementServerId = managementServerId; } + + public Boolean getPasswordChangeRequired() { + return passwordChangeRequired; + } + + public void setPasswordChangeRequired(Boolean passwordChangeRequired) { + this.passwordChangeRequired = passwordChangeRequired; + } } 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 8592a762c616..c1f24c63230d 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 @@ -31,52 +31,52 @@ @EntityReference(value = ManagementServerHost.class) public class ManagementServerResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the management server") + @Param(description = "The ID of the management server") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the management server") + @Param(description = "The name of the management server") private String name; @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the management server") + @Param(description = "The state of the management server") private State state; @SerializedName(ApiConstants.VERSION) - @Param(description = "the version of the management server") + @Param(description = "The version of the management server") private String version; @SerializedName(ApiConstants.JAVA_DISTRIBUTION) - @Param(description = "the java distribution name running the management server process") + @Param(description = "The java distribution name running the management server process") private String javaDistribution; @SerializedName(ApiConstants.JAVA_VERSION) - @Param(description = "the version of the java distribution running the management server process") + @Param(description = "The version of the java distribution running the management server process") private String javaVersion; @SerializedName(ApiConstants.OS_DISTRIBUTION) - @Param(description = "the name of the OS distribution running on the management server") + @Param(description = "The name of the OS distribution running on the management server") private String osDistribution; @SerializedName(ApiConstants.LAST_SERVER_START) - @Param(description = "the last time this Management Server was started") + @Param(description = "The last time this Management Server was started") private Date lastServerStart; @SerializedName(ApiConstants.LAST_SERVER_STOP) - @Param(description = "the last time this Management Server was stopped") + @Param(description = "The last time this Management Server was stopped") private Date lastServerStop; @SerializedName(ApiConstants.LAST_BOOT) - @Param(description = "the last time the host on which this Management Server runs was booted") + @Param(description = "The last time the host on which this Management Server runs was booted") private Date lastBoot; @SerializedName(ApiConstants.KERNEL_VERSION) - @Param(description = "the running OS kernel version for this Management Server") + @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.") + @Param(description = "The IP Address for this Management Server. This is deprecated, please use 'ipaddress' instead.") private String serviceIp; @SerializedName(ApiConstants.IP_ADDRESS) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/MigrationResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/MigrationResponse.java index c67b1d2d13ee..62934151b38b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/MigrationResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/MigrationResponse.java @@ -35,7 +35,7 @@ public class MigrationResponse extends BaseResponse { private String migrationType; @SerializedName("success") - @Param(description = "true if operation is executed successfully") + @Param(description = "True if operation is executed successfully") private boolean success; MigrationResponse() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NetworkACLItemResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NetworkACLItemResponse.java index f63cbbf4cb51..7af7ea2610b6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NetworkACLItemResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NetworkACLItemResponse.java @@ -30,51 +30,51 @@ @EntityReference(value = NetworkACLItem.class) public class NetworkACLItemResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the ACL Item") + @Param(description = "The ID of the ACL Item") private String id; @SerializedName(ApiConstants.PROTOCOL) - @Param(description = "the protocol of the ACL") + @Param(description = "The protocol of the ACL") private String protocol; @SerializedName(ApiConstants.START_PORT) - @Param(description = "the starting port of ACL's port range") + @Param(description = "The starting port of ACL's port range") private String startPort; @SerializedName(ApiConstants.END_PORT) - @Param(description = "the ending port of ACL's port range") + @Param(description = "The ending port of ACL's port range") private String endPort; @SerializedName(ApiConstants.TRAFFIC_TYPE) - @Param(description = "the traffic type for the ACL") + @Param(description = "The traffic type for the ACL") private String trafficType; @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the rule") + @Param(description = "The state of the rule") private String state; @SerializedName(ApiConstants.CIDR_LIST) - @Param(description = "the cidr list to forward traffic from. Multiple entries are separated by a single comma character (,).") + @Param(description = "The CIDR list to forward traffic from. Multiple entries are separated by a single comma character (,).") private String cidrList; @SerializedName(ApiConstants.ICMP_TYPE) - @Param(description = "type of the icmp message being sent") + @Param(description = "Type of the icmp message being sent") private Integer icmpType; @SerializedName(ApiConstants.ICMP_CODE) - @Param(description = "error code for this icmp message") + @Param(description = "Error code for this icmp message") private Integer icmpCode; @SerializedName(ApiConstants.TAGS) - @Param(description = "the list of resource tags associated with the network ACLs", responseObject = ResourceTagResponse.class) + @Param(description = "The list of resource tags associated with the Network ACLs", responseObject = ResourceTagResponse.class) private List tags; @SerializedName(ApiConstants.ACL_ID) - @Param(description = "the ID of the ACL this item belongs to") + @Param(description = "The ID of the ACL this item belongs to") private String aclId; @SerializedName(ApiConstants.ACL_NAME) - @Param(description = "the name of the ACL this item belongs to") + @Param(description = "The name of the ACL this item belongs to") private String aclName; @SerializedName(ApiConstants.NUMBER) @@ -86,11 +86,11 @@ public class NetworkACLItemResponse extends BaseResponse { private String action; @SerializedName(ApiConstants.FOR_DISPLAY) - @Param(description = "is rule for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) + @Param(description = "Is rule for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; @SerializedName(ApiConstants.ACL_REASON) - @Param(description = "an explanation on why this ACL rule is being applied", since = "4.12") + @Param(description = "An explanation on why this ACL rule is being applied", since = "4.12") private String reason; public void setId(String id) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NetworkACLResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NetworkACLResponse.java index 72ee80a2b0a0..7c120c59c810 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NetworkACLResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NetworkACLResponse.java @@ -28,11 +28,11 @@ @EntityReference(value = NetworkACL.class) public class NetworkACLResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the ACL") + @Param(description = "The ID of the ACL") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the Name of the ACL") + @Param(description = "The Name of the ACL") private String name; @SerializedName(ApiConstants.DESCRIPTION) @@ -48,7 +48,7 @@ public class NetworkACLResponse extends BaseResponse { private String vpcName; @SerializedName(ApiConstants.FOR_DISPLAY) - @Param(description = "is ACL for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) + @Param(description = "Is ACL for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; public void setId(String id) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NetworkDeviceResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NetworkDeviceResponse.java index a991c7ae2b05..0d6f7be45575 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NetworkDeviceResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NetworkDeviceResponse.java @@ -25,7 +25,7 @@ public class NetworkDeviceResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the network device") + @Param(description = "The ID of the Network device") private String id; public String getId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NetworkOfferingResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NetworkOfferingResponse.java index 81a8129ecb76..87f960590283 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NetworkOfferingResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NetworkOfferingResponse.java @@ -32,71 +32,71 @@ @SuppressWarnings("unused") public class NetworkOfferingResponse extends BaseResponseWithAnnotations { @SerializedName("id") - @Param(description = "the id of the network offering") + @Param(description = "The ID of the Network offering") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the network offering") + @Param(description = "The name of the Network offering") private String name; @SerializedName(ApiConstants.DISPLAY_TEXT) - @Param(description = "an alternate display text of the network offering.") + @Param(description = "An alternate display text of the Network offering.") private String displayText; @SerializedName(ApiConstants.TAGS) - @Param(description = "the tags for the network offering") + @Param(description = "The tags for the Network offering") private String tags; @SerializedName(ApiConstants.CREATED) - @Param(description = "the date this network offering was created") + @Param(description = "The date this Network offering was created") private Date created; @SerializedName(ApiConstants.TRAFFIC_TYPE) - @Param(description = "the traffic type for the network offering, supported types are Public, Management, Control, Guest, Vlan or Storage.") + @Param(description = "The traffic type for the Network offering, supported types are Public, Management, Control, Guest, VLAN or Storage.") private String trafficType; @SerializedName(ApiConstants.IS_DEFAULT) - @Param(description = "true if network offering is default, false otherwise") + @Param(description = "True if Network offering is default, false otherwise") private Boolean isDefault; @SerializedName(ApiConstants.SPECIFY_VLAN) - @Param(description = "true if network offering supports vlans, false otherwise") + @Param(description = "True if Network offering supports VLANs, false otherwise") private Boolean specifyVlan; @SerializedName(ApiConstants.CONSERVE_MODE) - @Param(description = "true if network offering is ip conserve mode enabled") + @Param(description = "True if Network offering is IP conserve mode enabled") private Boolean conserveMode; @SerializedName(ApiConstants.SPECIFY_IP_RANGES) - @Param(description = "true if network offering supports specifying ip ranges, false otherwise") + @Param(description = "True if Network offering supports specifying IP ranges, false otherwise") private Boolean specifyIpRanges; @SerializedName(ApiConstants.AVAILABILITY) - @Param(description = "availability of the network offering") + @Param(description = "Availability of the Network offering") private String availability; @SerializedName(ApiConstants.NETWORKRATE) - @Param(description = "data transfer rate in megabits per second allowed.") + @Param(description = "Data transfer rate in megabits per second allowed.") private Integer networkRate; @SerializedName(ApiConstants.STATE) - @Param(description = "state of the network offering. Can be Disabled/Enabled/Inactive") + @Param(description = "State of the Network offering. Can be Disabled/Enabled/Inactive") private String state; @SerializedName(ApiConstants.GUEST_IP_TYPE) - @Param(description = "guest type of the network offering, can be Shared or Isolated") + @Param(description = "Guest type of the Network offering, can be Shared or Isolated") private String guestIpType; @SerializedName(ApiConstants.SERVICE_OFFERING_ID) - @Param(description = "the ID of the service offering used by virtual router provider") + @Param(description = "The ID of the service offering used by virtual router provider") private String serviceOfferingId; @SerializedName(ApiConstants.SERVICE) - @Param(description = "the list of supported services", responseObject = ServiceResponse.class) + @Param(description = "The list of supported services", responseObject = ServiceResponse.class) private List services; @SerializedName(ApiConstants.FOR_VPC) - @Param(description = "true if network offering can be used by VPC networks only") + @Param(description = "True if Network offering can be used by VPC Networks only") private Boolean forVpc; @SerializedName(ApiConstants.FOR_NSX) @@ -104,7 +104,7 @@ public class NetworkOfferingResponse extends BaseResponseWithAnnotations { private Boolean forNsx; @SerializedName(ApiConstants.FOR_TUNGSTEN) - @Param(description = "true if network offering can be used by Tungsten-Fabric networks only") + @Param(description = "True if Network offering can be used by Tungsten-Fabric Networks only") private Boolean forTungsten; @SerializedName(ApiConstants.NETWORK_MODE) @@ -112,27 +112,27 @@ public class NetworkOfferingResponse extends BaseResponseWithAnnotations { private String networkMode; @SerializedName(ApiConstants.IS_PERSISTENT) - @Param(description = "true if network offering supports persistent networks, false otherwise") + @Param(description = "True if Network offering supports persistent networks, false otherwise") private Boolean isPersistent; @SerializedName(ApiConstants.DETAILS) - @Param(description = "additional key/value details tied with network offering", since = "4.2.0") + @Param(description = "Additional key/value details tied with network offering", since = "4.2.0") private Map details; @SerializedName(ApiConstants.EGRESS_DEFAULT_POLICY) - @Param(description = "true if guest network default egress policy is allow; false if default egress policy is deny") + @Param(description = "True if guest network default egress policy is allow; false if default egress policy is deny") private Boolean egressDefaultPolicy; @SerializedName(ApiConstants.MAX_CONNECTIONS) - @Param(description = "maximum number of concurrents connections to be handled by lb") + @Param(description = "Maximum number of concurrent connections to be handled by LB") private Integer concurrentConnections; @SerializedName(ApiConstants.SUPPORTS_STRECHED_L2_SUBNET) - @Param(description = "true if network offering supports network that span multiple zones", since = "4.4") + @Param(description = "True if network offering supports network that span multiple zones", since = "4.4") private Boolean supportsStrechedL2Subnet; @SerializedName(ApiConstants.SUPPORTS_PUBLIC_ACCESS) - @Param(description = "true if network offering supports public access for guest networks", since = "4.10.0") + @Param(description = "True if network offering supports public access for guest networks", since = "4.10.0") private Boolean supportsPublicAccess; @SerializedName(ApiConstants.SUPPORTS_INTERNAL_LB) @@ -140,23 +140,23 @@ public class NetworkOfferingResponse extends BaseResponseWithAnnotations { private Boolean supportsInternalLb; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID(s) this disk offering belongs to. Ignore this information as it is not currently applicable.") + @Param(description = "The domain ID(s) this disk offering belongs to. Ignore this information as it is not currently applicable.") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name(s) this disk offering belongs to. Ignore this information as it is not currently applicable.") + @Param(description = "The domain name(s) this disk offering belongs to. Ignore this information as it is not currently applicable.") private String domain; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the zone ID(s) this disk offering belongs to. Ignore this information as it is not currently applicable.", since = "4.13.0") + @Param(description = "The zone ID(s) this disk offering belongs to. Ignore this information as it is not currently applicable.", since = "4.13.0") private String zoneId; @SerializedName(ApiConstants.ZONE) - @Param(description = "the zone name(s) this disk offering belongs to. Ignore this information as it is not currently applicable.", since = "4.13.0") + @Param(description = "The zone name(s) this disk offering belongs to. Ignore this information as it is not currently applicable.", since = "4.13.0") private String zone; @SerializedName(ApiConstants.INTERNET_PROTOCOL) - @Param(description = "the internet protocol of the network offering") + @Param(description = "The internet protocol of the network offering") private String internetProtocol; @SerializedName(ApiConstants.SPECIFY_AS_NUMBER) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NetworkPermissionsResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NetworkPermissionsResponse.java index fcecd14e2820..27be4a51514d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NetworkPermissionsResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NetworkPermissionsResponse.java @@ -29,31 +29,31 @@ @SuppressWarnings("unused") public class NetworkPermissionsResponse extends BaseResponse { @SerializedName(ApiConstants.NETWORK_ID) - @Param(description = "the network ID") + @Param(description = "The Network ID") private String networkId; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the ID of the domain to which the network belongs") + @Param(description = "The ID of the domain to which the Network belongs") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the name of the domain to which the network belongs") + @Param(description = "The name of the domain to which the Network belongs") private String domainName; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account the network is available for") + @Param(description = "The Account the Network is available for") private String accountName; @SerializedName(ApiConstants.ACCOUNT_ID) - @Param(description = "the ID of account the network is available for") + @Param(description = "The ID of Account the Network is available for") private String accountId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project the network is available for") + @Param(description = "The project the Network is available for") private String projectName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the ID of project the network is available for") + @Param(description = "The ID of project the Network is available for") private String projectId; 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 d5db8b4dabbf..aeaf333540c7 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 @@ -36,103 +36,103 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implements ControlledEntityResponse, SetResourceIconResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the id of the network") + @Param(description = "The ID of the Network") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the network") + @Param(description = "The name of the Network") private String name; @SerializedName(ApiConstants.DISPLAY_TEXT) - @Param(description = "the displaytext of the network") + @Param(description = "The displaytext of the Network") private String displaytext; @SerializedName("broadcastdomaintype") - @Param(description = "Broadcast domain type of the network") + @Param(description = "Broadcast domain type of the Network") private String broadcastDomainType; @SerializedName(ApiConstants.TRAFFIC_TYPE) - @Param(description = "the traffic type of the network") + @Param(description = "The traffic type of the Network") private String trafficType; @SerializedName(ApiConstants.GATEWAY) - @Param(description = "the network's gateway") + @Param(description = "The Network's gateway") private String gateway; @SerializedName(ApiConstants.NETMASK) - @Param(description = "the network's netmask") + @Param(description = "The Network's netmask") private String netmask; @SerializedName(ApiConstants.CIDR) - @Param(description = "Cloudstack managed address space, all CloudStack managed VMs get IP address from CIDR") + @Param(description = "CloudStack managed address space, all CloudStack managed Instances get IP address from CIDR") private String cidr; @SerializedName(ApiConstants.NETWORK_CIDR) - @Param(description = "the network CIDR of the guest network configured with IP reservation. It is the summation of CIDR and RESERVED_IP_RANGE") + @Param(description = "The Network CIDR of the guest Network configured with IP reservation. It is the summation of CIDR and RESERVED_IP_RANGE") private String networkCidr; @SerializedName(ApiConstants.RESERVED_IP_RANGE) - @Param(description = "the network's IP range not to be used by CloudStack guest VMs and can be used for non CloudStack purposes") + @Param(description = "The Network's IP range not to be used by CloudStack guest Instances and can be used for non CloudStack purposes") private String reservedIpRange; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "zone id of the network") + @Param(description = "Zone ID of the Network") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "the name of the zone the network belongs to") + @Param(description = "The name of the zone the Network belongs to") private String zoneName; @SerializedName("networkofferingid") - @Param(description = "network offering id the network is created from") + @Param(description = "Network offering ID the Network is created from") private String networkOfferingId; @SerializedName("networkofferingname") - @Param(description = "name of the network offering the network is created from") + @Param(description = "Name of the Network offering the Network is created from") private String networkOfferingName; @SerializedName("networkofferingdisplaytext") - @Param(description = "display text of the network offering the network is created from") + @Param(description = "Display text of the Network offering the Network is created from") private String networkOfferingDisplayText; @SerializedName("networkofferingconservemode") - @Param(description = "true if network offering is ip conserve mode enabled") + @Param(description = "True if Network offering is IP conserve mode enabled") private Boolean networkOfferingConserveMode; @SerializedName("networkofferingavailability") - @Param(description = "availability of the network offering the network is created from") + @Param(description = "Availability of the Network offering the Network is created from") private String networkOfferingAvailability; @SerializedName(ApiConstants.IS_SYSTEM) - @Param(description = "true if network is system, false otherwise") + @Param(description = "True if Network is system, false otherwise") private Boolean isSystem; @SerializedName(ApiConstants.STATE) - @Param(description = "state of the network") + @Param(description = "State of the Network") private String state; @SerializedName("related") - @Param(description = "related to what other network configuration") + @Param(description = "Related to what other Network configuration") private String related; @SerializedName("broadcasturi") - @Param(description = "broadcast uri of the network. This parameter is visible to ROOT admins only") + @Param(description = "Broadcast URI of the Network. This parameter is visible to ROOT admins only") private String broadcastUri; @SerializedName(ApiConstants.DNS1) - @Param(description = "the first IPv4 DNS for the network") + @Param(description = "The first IPv4 DNS for the Network") private String dns1; @SerializedName(ApiConstants.DNS2) - @Param(description = "the second IPv4 DNS for the network") + @Param(description = "The second IPv4 DNS for the Network") private String dns2; @SerializedName(ApiConstants.TYPE) - @Param(description = "the type of the network") + @Param(description = "The type of the Network") private String type; @SerializedName(ApiConstants.VLAN) - @Param(description = "The vlan of the network. This parameter is visible to ROOT admins only") + @Param(description = "The VLAN of the Network. This parameter is visible to ROOT admins only") private String vlan; @SerializedName(ApiConstants.AS_NUMBER_ID) @@ -144,131 +144,131 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement private Long asNumber; @SerializedName(ApiConstants.ACL_TYPE) - @Param(description = "acl type - access type to the network") + @Param(description = "ACL type - access type to the Network") private String aclType; @SerializedName(ApiConstants.SUBDOMAIN_ACCESS) - @Param(description = "true if users from subdomains can access the domain level network") + @Param(description = "True if users from subdomains can access the domain level Network") private Boolean subdomainAccess; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the owner of the network") + @Param(description = "The owner of the Network") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the ipaddress") + @Param(description = "The project ID of the IP address") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the address") + @Param(description = "The project name of the address") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain id of the network owner") + @Param(description = "The domain ID of the Network owner") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name of the network owner") + @Param(description = "The domain name of the Network owner") private String domain; @SerializedName(ApiConstants.DOMAIN_PATH) - @Param(description = "path of the Domain the network belongs to", since = "4.19.0.0") + @Param(description = "Path of the Domain the network belongs to", since = "4.19.0.0") private String domainPath; @SerializedName("isdefault") - @Param(description = "true if network is default, false otherwise") + @Param(description = "True if Network is default, false otherwise") private Boolean isDefault; @SerializedName("service") - @Param(description = "the list of services", responseObject = ServiceResponse.class) + @Param(description = "The list of services", responseObject = ServiceResponse.class) private List services; @SerializedName(ApiConstants.NETWORK_DOMAIN) - @Param(description = "the network domain") + @Param(description = "The Network domain") private String networkDomain; @SerializedName(ApiConstants.PHYSICAL_NETWORK_ID) - @Param(description = "the physical network id") + @Param(description = "The physical Network id") private String physicalNetworkId; @SerializedName(ApiConstants.RESTART_REQUIRED) - @Param(description = "true network requires restart") + @Param(description = "True if Network requires restart") private Boolean restartRequired; @SerializedName(ApiConstants.SPECIFY_VLAN) - @Param(description = "true if network supports specifying vlan, false otherwise") + @Param(description = "True if network supports specifying vlan, false otherwise") private Boolean specifyVlan; @SerializedName(ApiConstants.SPECIFY_IP_RANGES) - @Param(description = "true if network supports specifying ip ranges, false otherwise") + @Param(description = "True if Network supports specifying IP ranges, false otherwise") private Boolean specifyIpRanges; @SerializedName(ApiConstants.VPC_ID) - @Param(description = "VPC the network belongs to") + @Param(description = "VPC the Network belongs to") private String vpcId; @SerializedName(ApiConstants.VPC_NAME) - @Param(description = "Name of the VPC to which this network belongs", since = "4.15") + @Param(description = "Name of the VPC to which this Network belongs", since = "4.15") private String vpcName; @SerializedName(ApiConstants.TUNGSTEN_VIRTUAL_ROUTER_UUID) - @Param(description = "Tungsten-Fabric virtual router the network belongs to") + @Param(description = "Tungsten-Fabric virtual router the Network belongs to") private String tungstenVirtualRouterUuid; @SerializedName(ApiConstants.CAN_USE_FOR_DEPLOY) - @Param(description = "list networks available for vm deployment") + @Param(description = "List Networks available for Instance deployment") private Boolean canUseForDeploy; @SerializedName(ApiConstants.IS_PERSISTENT) - @Param(description = "list networks that are persistent") + @Param(description = "List Networks that are persistent") private Boolean isPersistent; @SerializedName(ApiConstants.TAGS) - @Param(description = "the list of resource tags associated with network", responseObject = ResourceTagResponse.class) + @Param(description = "The list of resource tags associated with Network", responseObject = ResourceTagResponse.class) private List tags; @SerializedName(ApiConstants.DETAILS) - @Param(description = "the details of the network") + @Param(description = "The details of the Network") private Map details; @SerializedName(ApiConstants.IP6_GATEWAY) - @Param(description = "the gateway of IPv6 network") + @Param(description = "The gateway of IPv6 Network") private String ip6Gateway; @SerializedName(ApiConstants.IP6_CIDR) - @Param(description = "the cidr of IPv6 network") + @Param(description = "The CIDR of IPv6 Network") private String ip6Cidr; @SerializedName(ApiConstants.DISPLAY_NETWORK) - @Param(description = "an optional field, whether to the display the network to the end user or not.", authorized = {RoleType.Admin}) + @Param(description = "An optional field, whether to the display the Network to the end user or not.", authorized = {RoleType.Admin}) private Boolean displayNetwork; @SerializedName(ApiConstants.ACL_ID) - @Param(description = "ACL Id associated with the VPC network") + @Param(description = "ACL ID associated with the VPC Network") private String aclId; @SerializedName(ApiConstants.ACL_NAME) - @Param(description = "ACL name associated with the VPC network") + @Param(description = "ACL name associated with the VPC Network") private String aclName; @SerializedName(ApiConstants.STRECHED_L2_SUBNET) - @Param(description = "true if network can span multiple zones", since = "4.4") + @Param(description = "True if Network can span multiple zones", since = "4.4") private Boolean strechedL2Subnet; @SerializedName(ApiConstants.NETWORK_SPANNED_ZONES) - @Param(description = "If a network is enabled for 'streched l2 subnet' then represents zones on which network currently spans", since = "4.4") + @Param(description = "If a Network is enabled for 'stretched L2 subnet' then represents zones on which Network currently spans", since = "4.4") private Set networkSpannedZones; @SerializedName(ApiConstants.EXTERNAL_ID) - @Param(description = "The external id of the network", since = "4.11") + @Param(description = "The external ID of the Network", since = "4.11") private String externalId; @SerializedName(ApiConstants.REDUNDANT_ROUTER) - @Param(description = "If the network has redundant routers enabled", since = "4.11.1") + @Param(description = "If the Network has redundant routers enabled", since = "4.11.1") private Boolean redundantRouter; @SerializedName(ApiConstants.SUPPORTS_VM_AUTOSCALING) - @Param(description = "if network offering supports vm autoscaling feature", since = "4.18.0") + @Param(description = "If Network offering supports Instance autoscaling feature", since = "4.18.0") private Boolean supportsVmAutoScaling; @SerializedName(ApiConstants.RESOURCE_ICON) @@ -276,35 +276,35 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement ResourceIconResponse icon; @SerializedName(ApiConstants.CREATED) - @Param(description = "the date this network was created", since = "4.16.0") + @Param(description = "The date this Network was created", since = "4.16.0") private Date created; @SerializedName(ApiConstants.RECEIVED_BYTES) - @Param(description = "the total number of network traffic bytes received") + @Param(description = "The total number of Network traffic bytes received") private Long bytesReceived; @SerializedName(ApiConstants.SENT_BYTES) - @Param(description = "the total number of network traffic bytes sent") + @Param(description = "The total number of Network traffic bytes sent") private Long bytesSent; @SerializedName((ApiConstants.EGRESS_DEFAULT_POLICY)) - @Param(description = "true if guest network default egress policy is allow; false if default egress policy is deny") + @Param(description = "True if guest Network default egress policy is allow; false if default egress policy is deny") private Boolean egressDefaultPolicy; @SerializedName(ApiConstants.INTERNET_PROTOCOL) - @Param(description = "The internet protocol of network offering") + @Param(description = "The internet protocol of Network offering") private String internetProtocol; @SerializedName(ApiConstants.IPV6_ROUTING) - @Param(description = "The Ipv6 routing type of network offering", since = "4.17.0") + @Param(description = "The IPv6 routing type of network offering", since = "4.17.0") private String ipv6Routing; @SerializedName(ApiConstants.IPV6_ROUTES) - @Param(description = "The routes for the network to ease adding route in upstream router", since = "4.17.0") + @Param(description = "The routes for the Network to ease adding route in upstream router", since = "4.17.0") private Set ipv6Routes; @SerializedName(ApiConstants.PUBLIC_MTU) - @Param(description = "MTU configured on the network VR's public facing interfaces") + @Param(description = "MTU configured on the Network VR's public facing interfaces") private Integer publicMtu; @SerializedName(ApiConstants.PRIVATE_MTU) @@ -312,11 +312,11 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement private Integer privateMtu; @SerializedName(ApiConstants.IP6_DNS1) - @Param(description = "the first IPv6 DNS for the network", since = "4.18.0") + @Param(description = "The first IPv6 DNS for the network", since = "4.18.0") private String ipv6Dns1; @SerializedName(ApiConstants.IP6_DNS2) - @Param(description = "the second IPv6 DNS for the network", since = "4.18.0") + @Param(description = "The second IPv6 DNS for the network", since = "4.18.0") private String ipv6Dns2; @SerializedName(ApiConstants.IPV4_ROUTING) @@ -331,6 +331,18 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement @Param(description = "The BGP peers for the network", since = "4.20.0") private Set bgpPeers; + @SerializedName(ApiConstants.KEEP_MAC_ADDRESS_ON_PUBLIC_NIC) + @Param(description = ApiConstants.PARAMETER_DESCRIPTION_KEEP_MAC_ADDRESS_ON_PUBLIC_NIC, since = "4.23.0") + private Boolean keepMacAddressOnPublicNic; + + @SerializedName(ApiConstants.DNS_ZONE) + @Param(description = "DNS zone associated to the network", since = "4.23.0") + private String dnsZone; + + @SerializedName(ApiConstants.DNS_SUB_DOMAIN) + @Param(description = "DNS subdomain associated to the network", since = "4.23.0") + private String dnsSubdomain; + public NetworkResponse() {} public Boolean getDisplayNetwork() { @@ -702,4 +714,16 @@ public void setIpv6Dns1(String ipv6Dns1) { public void setIpv6Dns2(String ipv6Dns2) { this.ipv6Dns2 = ipv6Dns2; } + + public void setKeepMacAddressOnPublicNic(Boolean keepMacAddressOnPublicNic) { + this.keepMacAddressOnPublicNic = keepMacAddressOnPublicNic; + } + + public void setDnsZone(String dnsZone) { + this.dnsZone = dnsZone; + } + + public void setDnsSubdomain(String dnsSubdomain) { + this.dnsSubdomain = dnsSubdomain; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NicExtraDhcpOptionResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NicExtraDhcpOptionResponse.java index 4af89a37d8dd..7d0007373681 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NicExtraDhcpOptionResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NicExtraDhcpOptionResponse.java @@ -28,23 +28,23 @@ public class NicExtraDhcpOptionResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the extra dhcp option") + @Param(description = "The ID of the extra DHCP option") private String id; @SerializedName(ApiConstants.NIC_ID) - @Param(description = "the ID of the nic") + @Param(description = "The ID of the NIC") private String nicId; @SerializedName(ApiConstants.EXTRA_DHCP_OPTION_NAME) - @Param(description = "the name of the extra DHCP option") + @Param(description = "The name of the extra DHCP option") private String codeName; @SerializedName(ApiConstants.EXTRA_DHCP_OPTION_CODE) - @Param(description = "the extra DHCP option code") + @Param(description = "The extra DHCP option code") private int code; @SerializedName(ApiConstants.EXTRA_DHCP_OPTION_VALUE) - @Param(description = "the extra DHCP option value") + @Param(description = "The extra DHCP option value") private String value; public NicExtraDhcpOptionResponse() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NicResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NicResponse.java index 65e126de545f..5ad41ad62244 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NicResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NicResponse.java @@ -31,79 +31,79 @@ public class NicResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the nic") + @Param(description = "The ID of the NIC") private String id; @SerializedName(ApiConstants.NETWORK_ID) - @Param(description = "the ID of the corresponding network") + @Param(description = "The ID of the corresponding Network") private String networkId; @SerializedName(ApiConstants.NETWORK_NAME) - @Param(description = "the name of the corresponding network") + @Param(description = "The name of the corresponding Network") private String networkName; @SerializedName(ApiConstants.NETMASK) - @Param(description = "the netmask of the nic") + @Param(description = "The netmask of the NIC") private String netmask; @SerializedName(ApiConstants.GATEWAY) - @Param(description = "the gateway of the nic") + @Param(description = "The gateway of the NIC") private String gateway; @SerializedName(ApiConstants.IP_ADDRESS) - @Param(description = "the ip address of the nic") + @Param(description = "The IP address of the NIC") private String ipaddress; @SerializedName(ApiConstants.ISOLATION_URI) - @Param(description = "the isolation uri of the nic") + @Param(description = "The isolation URI of the NIC") private String isolationUri; @SerializedName(ApiConstants.BROADCAST_URI) - @Param(description = "the broadcast uri of the nic") + @Param(description = "The broadcast URI of the NIC") private String broadcastUri; @SerializedName(ApiConstants.TRAFFIC_TYPE) - @Param(description = "the traffic type of the nic") + @Param(description = "The traffic type of the NIC") private String trafficType; @SerializedName(ApiConstants.TYPE) - @Param(description = "the type of the nic") + @Param(description = "The type of the NIC") private String type; @SerializedName(ApiConstants.IS_DEFAULT) - @Param(description = "true if nic is default, false otherwise") + @Param(description = "True if NIC is default, false otherwise") private Boolean isDefault; @SerializedName(ApiConstants.MAC_ADDRESS) - @Param(description = "true if nic is default, false otherwise") + @Param(description = "True if NIC is default, false otherwise") private String macAddress; @SerializedName(ApiConstants.IP6_GATEWAY) - @Param(description = "the gateway of IPv6 network") + @Param(description = "The gateway of IPv6 Network") private String ip6Gateway; @SerializedName(ApiConstants.IP6_CIDR) - @Param(description = "the cidr of IPv6 network") + @Param(description = "The CIDR of IPv6 Network") private String ip6Cidr; @SerializedName(ApiConstants.IP6_ADDRESS) - @Param(description = "the IPv6 address of network") + @Param(description = "The IPv6 address of Network") private String ip6Address; @SerializedName(ApiConstants.SECONDARY_IP) - @Param(description = "the Secondary ipv4 addr of nic") + @Param(description = "The Secondary IPv4 addr of NIC") private List secondaryIps; @SerializedName(ApiConstants.EXTRA_DHCP_OPTION) - @Param(description = "the extra dhcp options on the nic", since = "4.11.0") + @Param(description = "The extra DHCP options on the NIC", since = "4.11.0") private List extraDhcpOptions; @SerializedName(ApiConstants.DEVICE_ID) - @Param(description = "device id for the network when plugged into the virtual machine", since = "4.4") + @Param(description = "Device ID for the Network when plugged into the Instance", since = "4.4") private String deviceId; @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) - @Param(description = "Id of the vm to which the nic belongs") + @Param(description = "Id of the Instance to which the NIC belongs") private String vmId; @SerializedName(ApiConstants.NSX_LOGICAL_SWITCH) @@ -119,11 +119,11 @@ public class NicResponse extends BaseResponse { private Integer vlanId; @SerializedName(ApiConstants.ISOLATED_PVLAN) - @Param(description = "the isolated private VLAN if available", since="4.14.0") + @Param(description = "The isolated private VLAN if available", since="4.14.0") private Integer isolatedPvlanId; @SerializedName(ApiConstants.ISOLATED_PVLAN_TYPE) - @Param(description = "the isolated private VLAN type if available", since="4.14.0") + @Param(description = "The isolated private VLAN type if available", since="4.14.0") private String isolatedPvlanType; @SerializedName(ApiConstants.ADAPTER_TYPE) @@ -131,7 +131,7 @@ public class NicResponse extends BaseResponse { private String adapterType; @SerializedName(ApiConstants.IP_ADDRESSES) - @Param(description = "IP addresses associated with NIC found for unmanaged VM", since="4.14.0") + @Param(description = "IP addresses associated with NIC found for unmanaged Instance", since="4.14.0") private List ipAddresses; @SerializedName(ApiConstants.MTU) @@ -139,13 +139,21 @@ public class NicResponse extends BaseResponse { private Integer mtu; @SerializedName(ApiConstants.PUBLIC_IP_ID) - @Param(description = "public IP address id associated with this nic via Static nat rule") + @Param(description = "Public IP address ID associated with this NIC via Static NAT rule") private String publicIpId; @SerializedName(ApiConstants.PUBLIC_IP) - @Param(description = "public IP address associated with this nic via Static nat rule") + @Param(description = "Public IP address associated with this NIC via Static NAT rule") private String publicIp; + @SerializedName(ApiConstants.ENABLED) + @Param(description = "whether the NIC is enabled or not") + private Boolean isEnabled; + + @SerializedName(ApiConstants.NIC_DNS_NAME) + @Param(description = "DNS name associated with this NIC's IP address") + private String nicDnsName; + public void setVmId(String vmId) { this.vmId = vmId; } @@ -223,11 +231,11 @@ public void setExtraDhcpOptions(List extraDhcpOption } @SerializedName(ApiConstants.VPC_ID) - @Param(description = "Id of the vpc to which the nic belongs") + @Param(description = "ID of the VPC to which the NIC belongs") private String vpcId; @SerializedName(ApiConstants.VPC_NAME) - @Param(description = "name of the vpc to which the nic belongs") + @Param(description = "Name of the VPC to which the NIC belongs") private String vpcName; @Override @@ -416,4 +424,16 @@ public void setPublicIpId(String publicIpId) { public void setPublicIp(String publicIp) { this.publicIp = publicIp; } + + public Boolean getEnabled() { + return isEnabled; + } + + public void setEnabled(Boolean enabled) { + isEnabled = enabled; + } + + public void setNicDnsName(String nicDnsName) { + this.nicDnsName = nicDnsName; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NicSecondaryIpResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NicSecondaryIpResponse.java index 467a1c9e987a..8734e65dfa58 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NicSecondaryIpResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NicSecondaryIpResponse.java @@ -30,11 +30,11 @@ public class NicSecondaryIpResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the secondary private IP addr") + @Param(description = "The ID of the secondary private IP addr") private String id; @SerializedName("secondaryip") - @Param(description = "the list of Secondary ipv4 addr of nic") + @Param(description = "The list of Secondary IPv4 addr of NIC") private List secondaryIpsList; @SerializedName(ApiConstants.IP_ADDRESS) @@ -42,17 +42,21 @@ public class NicSecondaryIpResponse extends BaseResponse { private String ipAddr; @SerializedName(ApiConstants.NIC_ID) - @Param(description = "the ID of the nic") + @Param(description = "The ID of the NIC") private String nicId; @SerializedName(ApiConstants.NETWORK_ID) - @Param(description = "the ID of the network") + @Param(description = "The ID of the Network") private String nwId; @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) - @Param(description = "the ID of the vm") + @Param(description = "The ID of the Instance") private String vmId; + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "Description of the secondary IP address") + private String description; + @Override public String getObjectId() { return this.getId(); @@ -98,6 +102,14 @@ public void setId(String id) { this.id = id; } + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + public List getSecondaryIpsList() { return secondaryIpsList; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/OutOfBandManagementResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/OutOfBandManagementResponse.java index 3a8f5fb55ab1..5ce75e44efa6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/OutOfBandManagementResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/OutOfBandManagementResponse.java @@ -28,47 +28,47 @@ @EntityReference(value = Host.class) public class OutOfBandManagementResponse extends BaseResponse { @SerializedName(ApiConstants.HOST_ID) - @Param(description = "the ID of the host") + @Param(description = "The ID of the host") private String id; @SerializedName(ApiConstants.POWER_STATE) - @Param(description = "the out-of-band management interface powerState of the host") + @Param(description = "The out-of-band management interface powerState of the host") private OutOfBandManagement.PowerState powerState; @SerializedName(ApiConstants.ENABLED) - @Param(description = "true if out-of-band management is enabled for the host") + @Param(description = "True if out-of-band management is enabled for the host") private Boolean enabled; @SerializedName(ApiConstants.DRIVER) - @Param(description = "the out-of-band management driver for the host") + @Param(description = "The out-of-band management driver for the host") private String driver; @SerializedName(ApiConstants.ADDRESS) - @Param(description = "the out-of-band management interface address") + @Param(description = "The out-of-band management interface address") private String ipAddress; @SerializedName(ApiConstants.PORT) - @Param(description = "the out-of-band management interface port") + @Param(description = "The out-of-band management interface port") private String port; @SerializedName(ApiConstants.USERNAME) - @Param(description = "the out-of-band management interface username") + @Param(description = "The out-of-band management interface username") private String username; @SerializedName(ApiConstants.PASSWORD) - @Param(description = "the out-of-band management interface password") + @Param(description = "The out-of-band management interface password") private String password; @SerializedName(ApiConstants.ACTION) - @Param(description = "the out-of-band management action (if issued)") + @Param(description = "The out-of-band management action (if issued)") private String outOfBandManagementAction; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "the operation result description") + @Param(description = "The operation result description") private String resultDescription; @SerializedName(ApiConstants.STATUS) - @Param(description = "the operation result") + @Param(description = "The operation result") private Boolean success; public OutOfBandManagementResponse() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/OvsProviderResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/OvsProviderResponse.java index 2b67e1618dc2..9ed7247e403e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/OvsProviderResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/OvsProviderResponse.java @@ -29,33 +29,33 @@ public class OvsProviderResponse extends BaseResponse implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the id of the ovs") + @Param(description = "The ID of the ovs") private String id; @SerializedName(ApiConstants.NSP_ID) - @Param(description = "the physical network service provider id of the provider") + @Param(description = "The physical Network service provider id of the provider") private String nspId; @SerializedName(ApiConstants.ENABLED) @Param(description = "Enabled/Disabled the service provider") private Boolean enabled; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account associated with the provider") + @Param(description = "The Account associated with the provider") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the ipaddress") + @Param(description = "The project ID of the IP address") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the address") + @Param(description = "The project name of the address") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID associated with the provider") + @Param(description = "The domain ID associated with the provider") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain associated with the provider") + @Param(description = "The domain associated with the provider") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) - @Param(description = "path of the domain to which the provider belongs", since = "4.19.2.0") + @Param(description = "Path of the domain to which the provider belongs", since = "4.19.2.0") private String domainPath; @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/response/PhysicalNetworkResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/PhysicalNetworkResponse.java index f5116829305a..46cf233e279b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/PhysicalNetworkResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/PhysicalNetworkResponse.java @@ -32,47 +32,47 @@ public class PhysicalNetworkResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the uuid of the physical network") + @Param(description = "The UUID of the physical Network") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "name of the physical network") + @Param(description = "Name of the physical Network") private String name; @SerializedName(ApiConstants.BROADCAST_DOMAIN_RANGE) - @Param(description = "Broadcast domain range of the physical network") + @Param(description = "Broadcast domain range of the physical Network") private String broadcastDomainRange; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "zone id of the physical network") + @Param(description = "Zone ID of the physical Network") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "zone name of the physical network") + @Param(description = "Zone name of the physical Network") private String zoneName; @SerializedName(ApiConstants.STATE) - @Param(description = "state of the physical network") + @Param(description = "State of the physical Network") private String state; @SerializedName(ApiConstants.VLAN) - @Param(description = "the vlan of the physical network") + @Param(description = "The VLAN of the physical Network") private String vlan; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain id of the physical network owner") + @Param(description = "The domain ID of the physical Network owner") private String domainId; @SerializedName(ApiConstants.TAGS) - @Param(description = "comma separated tag") + @Param(description = "Comma separated tag") private String tags; @SerializedName(ApiConstants.ISOLATION_METHODS) - @Param(description = "isolation methods") + @Param(description = "Isolation methods") private String isolationMethods; @SerializedName(ApiConstants.NETWORK_SPEED) - @Param(description = "the speed of the physical network") + @Param(description = "The speed of the physical Network") private String networkSpeed; @Override 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 6a1afaecbcfc..51cad6381f73 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 @@ -30,59 +30,59 @@ @EntityReference(value = Pod.class) public class PodResponse extends BaseResponseWithAnnotations { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the Pod") + @Param(description = "The ID of the Pod") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the Pod") + @Param(description = "The name of the Pod") private String name; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the Zone ID of the Pod") + @Param(description = "The Zone ID of the Pod") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "the Zone name of the Pod") + @Param(description = "The Zone name of the Pod") private String zoneName; @SerializedName(ApiConstants.GATEWAY) - @Param(description = "the gateway of the Pod") + @Param(description = "The gateway of the Pod") private String gateway; @SerializedName(ApiConstants.NETMASK) - @Param(description = "the netmask of the Pod") + @Param(description = "The netmask of the Pod") private String netmask; @SerializedName(ApiConstants.IP_RANGES) - @Param(description = "the IP ranges for the Pod", responseObject = IpRangeResponse.class, since = "4.16.0") + @Param(description = "The IP ranges for the Pod", responseObject = IpRangeResponse.class, since = "4.16.0") private List ipRanges; @Deprecated(since = "4.16") @SerializedName(ApiConstants.START_IP) - @Param(description = "the starting IP for the Pod. This parameter is deprecated, please use 'startip' from ipranges parameter.") + @Param(description = "The starting IP for the Pod. This parameter is deprecated, please use 'startip' from ipranges parameter.") private List startIp; @Deprecated(since = "4.16") @SerializedName(ApiConstants.END_IP) - @Param(description = "the ending IP for the Pod. This parameter is deprecated, please use 'endip' from ipranges parameter.") + @Param(description = "The ending IP for the Pod. This parameter is deprecated, please use 'endip' from ipranges parameter.") private List endIp; @Deprecated(since = "4.16") @SerializedName(ApiConstants.FOR_SYSTEM_VMS) - @Param(description = "indicates if range is dedicated for CPVM and SSVM. This parameter is deprecated, please use 'forsystemvms' from ipranges parameter.") + @Param(description = "Indicates if range is dedicated for CPVM and SSVM. This parameter is deprecated, please use 'forsystemvms' from ipranges parameter.") private List forSystemVms; @Deprecated(since = "4.16") @SerializedName(ApiConstants.VLAN_ID) - @Param(description = "indicates Vlan ID for the range. This parameter is deprecated, please use 'vlanid' from ipranges parameter.") + @Param(description = "Indicates VLAN ID for the range. This parameter is deprecated, please use 'vlanid' from ipranges parameter.") private List vlanId; @SerializedName(ApiConstants.ALLOCATION_STATE) - @Param(description = "the allocation state of the Pod") + @Param(description = "The allocation state of the Pod") private String allocationState; @SerializedName(ApiConstants.CAPACITY) - @Param(description = "the capacity of the Pod", responseObject = CapacityResponse.class) + @Param(description = "The capacity of the Pod", responseObject = CapacityResponse.class) private List capacities; @SerializedName(ApiConstants.STORAGE_ACCESS_GROUPS) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/PortableIpRangeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/PortableIpRangeResponse.java index ddb2f9052d87..45316c8b642f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/PortableIpRangeResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/PortableIpRangeResponse.java @@ -32,31 +32,31 @@ public class PortableIpRangeResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "portable IP range ID") + @Param(description = "Portable IP range ID") private String id; @SerializedName(ApiConstants.REGION_ID) - @Param(description = "Region Id in which portable ip range is provisioned") + @Param(description = "Region ID in which portable IP range is provisioned") private Integer regionId; @SerializedName(ApiConstants.GATEWAY) - @Param(description = "the gateway of the VLAN IP range") + @Param(description = "The gateway of the VLAN IP range") private String gateway; @SerializedName(ApiConstants.NETMASK) - @Param(description = "the netmask of the VLAN IP range") + @Param(description = "The netmask of the VLAN IP range") private String netmask; @SerializedName(ApiConstants.VLAN) - @Param(description = "the ID or VID of the VLAN.") + @Param(description = "The ID or VID of the VLAN.") private String vlan; @SerializedName(ApiConstants.START_IP) - @Param(description = "the start ip of the portable IP range") + @Param(description = "The start IP of the portable IP range") private String startIp; @SerializedName(ApiConstants.END_IP) - @Param(description = "the end ip of the portable IP range") + @Param(description = "The end IP of the portable IP range") private String endIp; @SerializedName(ApiConstants.PORTABLE_IP_ADDRESS) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/PortableIpResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/PortableIpResponse.java index e477b1115616..98279aaa1c57 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/PortableIpResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/PortableIpResponse.java @@ -32,43 +32,43 @@ public class PortableIpResponse extends BaseResponse { @SerializedName(ApiConstants.REGION_ID) - @Param(description = "Region Id in which global load balancer is created") + @Param(description = "Region ID in which global Load balancer is created") private Integer regionId; @SerializedName(ApiConstants.IP_ADDRESS) - @Param(description = "public IP address") + @Param(description = "Public IP address") private String ipAddress; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the ID of the zone the public IP address belongs to") + @Param(description = "The ID of the zone the public IP address belongs to") private String zoneId; @SerializedName(ApiConstants.NETWORK_ID) - @Param(description = "the ID of the Network where ip belongs to") + @Param(description = "The ID of the Network where IP belongs to") private String networkId; @SerializedName(ApiConstants.VPC_ID) - @Param(description = "VPC the ip belongs to") + @Param(description = "VPC the IP belongs to") private String vpcId; @SerializedName(ApiConstants.PHYSICAL_NETWORK_ID) - @Param(description = "the physical network this belongs to") + @Param(description = "The physical Network this belongs to") private String physicalNetworkId; @SerializedName(ApiConstants.ACCOUNT_ID) - @Param(description = "the account ID the portable IP address is associated with") + @Param(description = "The Account ID the portable IP address is associated with") private String accountId; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID the portable IP address is associated with") + @Param(description = "The domain ID the portable IP address is associated with") private String domainId; @SerializedName("allocated") - @Param(description = "date the portal IP address was acquired") + @Param(description = "Date the portal IP address was acquired") private Date allocated; @SerializedName(ApiConstants.STATE) - @Param(description = "State of the ip address. Can be: Allocating, Allocated, Releasing and Free") + @Param(description = "State of the IP address. Can be: Allocating, Allocated, Releasing and Free") private String state; public void setRegionId(Integer regionId) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/PrivateGatewayResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/PrivateGatewayResponse.java index 414aed94bad3..7a6d443c14ad 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/PrivateGatewayResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/PrivateGatewayResponse.java @@ -30,35 +30,35 @@ public class PrivateGatewayResponse extends BaseResponseWithAssociatedNetwork implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the id of the private gateway") + @Param(description = "The ID of the private gateway") private String id; @SerializedName(ApiConstants.GATEWAY) - @Param(description = "the gateway") + @Param(description = "The gateway") private String gateway; @SerializedName(ApiConstants.NETMASK) - @Param(description = "the private gateway's netmask") + @Param(description = "The private gateway's netmask") private String netmask; @SerializedName(ApiConstants.IP_ADDRESS) - @Param(description = "the private gateway's ip address") + @Param(description = "The private gateway's IP address") private String address; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "zone id of the private gateway") + @Param(description = "Zone ID of the private gateway") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "the name of the zone the private gateway belongs to") + @Param(description = "The name of the zone the private gateway belongs to") private String zoneName; @SerializedName(ApiConstants.VLAN) - @Param(description = "the network implementation uri for the private gateway") + @Param(description = "The Network implementation uri for the private gateway") private String broadcastUri; @SerializedName(ApiConstants.VPC_ID) - @Param(description = "VPC id the private gateway belongs to") + @Param(description = "VPC ID the private gateway belongs to") private String vpcId; @SerializedName(ApiConstants.VPC_NAME) @@ -66,31 +66,31 @@ public class PrivateGatewayResponse extends BaseResponseWithAssociatedNetwork im private String vpcName; @SerializedName(ApiConstants.PHYSICAL_NETWORK_ID) - @Param(description = "the physical network id") + @Param(description = "The physical Network id") private String physicalNetworkId; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account associated with the private gateway") + @Param(description = "The Account associated with the private gateway") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the private gateway") + @Param(description = "The project ID of the private gateway") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the private gateway") + @Param(description = "The project name of the private gateway") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the ID of the domain associated with the private gateway") + @Param(description = "The ID of the domain associated with the private gateway") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain associated with the private gateway") + @Param(description = "The domain associated with the private gateway") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) - @Param(description = "path of the domain to which the private gateway belongs", since = "4.19.2.0") + @Param(description = "Path of the domain to which the private gateway belongs", since = "4.19.2.0") private String domainPath; @SerializedName(ApiConstants.STATE) @@ -102,7 +102,7 @@ public class PrivateGatewayResponse extends BaseResponseWithAssociatedNetwork im private Boolean sourceNat; @SerializedName(ApiConstants.ACL_ID) - @Param(description = "ACL Id set for private gateway") + @Param(description = "ACL ID set for private gateway") private String aclId; @SerializedName(ApiConstants.ACL_NAME) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ProjectAccountResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ProjectAccountResponse.java index d035622f65f1..89e0c38a48e8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ProjectAccountResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ProjectAccountResponse.java @@ -30,19 +30,19 @@ @SuppressWarnings("unused") public class ProjectAccountResponse extends BaseResponse implements ControlledViewEntityResponse { @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "project id") + @Param(description = "Project ID") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "project name") + @Param(description = "Project name") private String projectName; @SerializedName(ApiConstants.ACCOUNT_ID) - @Param(description = "the id of the account") + @Param(description = "The ID of the Account") private String accountId; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the name of the account") + @Param(description = "The name of the Account") private String accountName; @SerializedName(ApiConstants.USERNAME) @@ -50,35 +50,35 @@ public class ProjectAccountResponse extends BaseResponse implements ControlledVi private String username; @SerializedName(ApiConstants.ACCOUNT_TYPE) - @Param(description = "account type (admin, domain-admin, user)") + @Param(description = "Account type (admin, domain-admin, user)") private Integer accountType; @SerializedName(ApiConstants.USER_ID) - @Param(description = "Id of the user") + @Param(description = "ID of the user") private String userId; @SerializedName(ApiConstants.PROJECT_ROLE_ID) - @Param(description = "Id of the project role associated with the account/user") + @Param(description = "ID of the project role associated with the Account/User") private String projectRoleId; @SerializedName(ApiConstants.ROLE) - @Param(description = "account role in the project (regular,owner)") + @Param(description = "Account role in the project (regular, owner)") private String role; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "id of the Domain the account belongs too") + @Param(description = "ID of the Domain the Account belongs too") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "name of the Domain the account belongs too") + @Param(description = "Name of the Domain the Account belongs too") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) - @Param(description = "path of the Domain the account belongs to", since = "4.19.2.0") + @Param(description = "Path of the Domain the Account belongs to", since = "4.19.2.0") private String domainPath; @SerializedName(ApiConstants.USER) - @Param(description = "the list of users associated with account", responseObject = UserResponse.class) + @Param(description = "The list of users associated with Account", responseObject = UserResponse.class) private List users; @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ProjectInvitationResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ProjectInvitationResponse.java index ad3f99f6b3ff..c99e3edf6db1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ProjectInvitationResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ProjectInvitationResponse.java @@ -28,43 +28,43 @@ @SuppressWarnings("unused") public class ProjectInvitationResponse extends BaseResponse implements ControlledViewEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the id of the invitation") + @Param(description = "The ID of the invitation") private String id; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the id of the project") + @Param(description = "The ID of the project") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the name of the project") + @Param(description = "The name of the project") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain id the project belongs to") + @Param(description = "The domain ID the project belongs to") private String domainId; @SerializedName(ApiConstants.USER_ID) - @Param(description = "the User ID") + @Param(description = "The User ID") private String userId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name where the project belongs to") + @Param(description = "The domain name where the project belongs to") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) - @Param(description = "path of the Domain the project belongs to", since = "4.19.2.0") + @Param(description = "Path of the Domain the project belongs to", since = "4.19.2.0") private String domainPath; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account name of the project's owner") + @Param(description = "The Account name of the project's owner") private String accountName; @SerializedName(ApiConstants.EMAIL) - @Param(description = "the email the invitation was sent to") + @Param(description = "The email the invitation was sent to") private String email; @SerializedName(ApiConstants.STATE) - @Param(description = "the invitation state") + @Param(description = "The invitation state") private String invitationState; public void setId(String id) { 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 e72f6b860369..40f9405d0fc5 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 @@ -33,87 +33,87 @@ public class ProjectResponse extends BaseResponse implements ResourceLimitAndCountResponse, SetResourceIconResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the id of the project") + @Param(description = "The ID of the project") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the project") + @Param(description = "The name of the project") private String name; @SerializedName(ApiConstants.DISPLAY_TEXT) - @Param(description = "the displaytext of the project") + @Param(description = "The displaytext of the project") private String displaytext; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain id the project belongs to") + @Param(description = "The domain ID the project belongs to") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name where the project belongs to") + @Param(description = "The domain name where the project belongs to") private String domain; @SerializedName(ApiConstants.OWNER) - @Param(description = "the account name of the project's owners") + @Param(description = "The Account name of the project's owners") private List> owners; @SerializedName("projectaccountname") - @Param(description="the project account name of the project") + @Param(description = "The project Account name of the project") private String projectAccountName; @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the project") + @Param(description = "The state of the project") private String state; @SerializedName(ApiConstants.TAGS) - @Param(description = "the list of resource tags associated with vm", responseObject = ResourceTagResponse.class) + @Param(description = "The list of resource tags associated with Instance", responseObject = ResourceTagResponse.class) private List tags = new ArrayList(); @SerializedName("networklimit") - @Param(description = "the total number of networks the project can own", since = "4.2.0") + @Param(description = "The total number of Networks the project can own", since = "4.2.0") private String networkLimit; @SerializedName("networktotal") - @Param(description = "the total number of networks owned by project", since = "4.2.0") + @Param(description = "The total number of Networks owned by project", since = "4.2.0") private Long networkTotal; @SerializedName("networkavailable") - @Param(description = "the total number of networks available to be created for this project", since = "4.2.0") + @Param(description = "The total number of Networks available to be created for this project", since = "4.2.0") private String networkAvailable; @SerializedName("vpclimit") - @Param(description = "the total number of vpcs the project can own", since = "4.2.0") + @Param(description = "The total number of VPCs the project can own", since = "4.2.0") private String vpcLimit; @SerializedName("vpctotal") - @Param(description = "the total number of vpcs owned by project", since = "4.2.0") + @Param(description = "The total number of VPCs owned by project", since = "4.2.0") private Long vpcTotal; @SerializedName("vpcavailable") - @Param(description = "the total number of vpcs available to be created for this project", since = "4.2.0") + @Param(description = "The total number of VPCs available to be created for this project", since = "4.2.0") private String vpcAvailable; @SerializedName("cpulimit") - @Param(description = "the total number of cpu cores the project can own", since = "4.2.0") + @Param(description = "The total number of CPU cores the project can own", since = "4.2.0") private String cpuLimit; @SerializedName("cputotal") - @Param(description = "the total number of cpu cores owned by project", since = "4.2.0") + @Param(description = "The total number of CPU cores owned by project", since = "4.2.0") private Long cpuTotal; @SerializedName("cpuavailable") - @Param(description = "the total number of cpu cores available to be created for this project", since = "4.2.0") + @Param(description = "The total number of CPU cores available to be created for this project", since = "4.2.0") private String cpuAvailable; @SerializedName("memorylimit") - @Param(description = "the total memory (in MB) the project can own", since = "4.2.0") + @Param(description = "The total memory (in MB) the project can own", since = "4.2.0") private String memoryLimit; @SerializedName("memorytotal") - @Param(description = "the total memory (in MB) owned by project", since = "4.2.0") + @Param(description = "The total memory (in MB) owned by project", since = "4.2.0") private Long memoryTotal; @SerializedName("memoryavailable") - @Param(description = "the total memory (in MB) available to be created for this project", since = "4.2.0") + @Param(description = "The total memory (in MB) available to be created for this project", since = "4.2.0") private String memoryAvailable; @SerializedName("gpulimit") @@ -129,27 +129,27 @@ public class ProjectResponse extends BaseResponse implements ResourceLimitAndCou private String gpuAvailable; @SerializedName("primarystoragelimit") - @Param(description = "the total primary storage space (in GiB) the project can own", since = "4.2.0") + @Param(description = "The total primary storage space (in GiB) the project can own", since = "4.2.0") private String primaryStorageLimit; @SerializedName("primarystoragetotal") - @Param(description = "the total primary storage space (in GiB) owned by project", since = "4.2.0") + @Param(description = "The total primary storage space (in GiB) owned by project", since = "4.2.0") private Long primaryStorageTotal; @SerializedName("primarystorageavailable") - @Param(description = "the total primary storage space (in GiB) available to be used for this project", since = "4.2.0") + @Param(description = "The total primary storage space (in GiB) available to be used for this project", since = "4.2.0") private String primaryStorageAvailable; @SerializedName("secondarystoragelimit") - @Param(description = "the total secondary storage space (in GiB) the project can own", since = "4.2.0") + @Param(description = "The total secondary storage space (in GiB) the project can own", since = "4.2.0") private String secondaryStorageLimit; @SerializedName("secondarystoragetotal") - @Param(description = "the total secondary storage space (in GiB) owned by project", since = "4.2.0") + @Param(description = "The total secondary storage space (in GiB) owned by project", since = "4.2.0") private float secondaryStorageTotal; @SerializedName("secondarystorageavailable") - @Param(description = "the total secondary storage space (in GiB) available to be used for this project", since = "4.2.0") + @Param(description = "The total secondary storage space (in GiB) available to be used for this project", since = "4.2.0") private String secondaryStorageAvailable; @SerializedName(ApiConstants.BUCKET_LIMIT) @@ -177,51 +177,51 @@ public class ProjectResponse extends BaseResponse implements ResourceLimitAndCou private String objectStorageAvailable; @SerializedName(ApiConstants.VM_LIMIT) - @Param(description = "the total number of virtual machines that can be deployed by this project", since = "4.2.0") + @Param(description = "The total number of Instances that can be deployed by this project", since = "4.2.0") private String vmLimit; @SerializedName(ApiConstants.VM_TOTAL) - @Param(description = "the total number of virtual machines deployed by this project", since = "4.2.0") + @Param(description = "The total number of Instances deployed by this project", since = "4.2.0") private Long vmTotal; @SerializedName(ApiConstants.VM_AVAILABLE) - @Param(description = "the total number of virtual machines available for this project to acquire", since = "4.2.0") + @Param(description = "The total number of Instances available for this project to acquire", since = "4.2.0") private String vmAvailable; @SerializedName(ApiConstants.IP_LIMIT) - @Param(description = "the total number of public ip addresses this project can acquire", since = "4.2.0") + @Param(description = "The total number of public IP addresses this project can acquire", since = "4.2.0") private String ipLimit; @SerializedName(ApiConstants.IP_TOTAL) - @Param(description = "the total number of public ip addresses allocated for this project", since = "4.2.0") + @Param(description = "The total number of public IP addresses allocated for this project", since = "4.2.0") private Long ipTotal; @SerializedName(ApiConstants.IP_AVAILABLE) - @Param(description = "the total number of public ip addresses available for this project to acquire", since = "4.2.0") + @Param(description = "The total number of public IP addresses available for this project to acquire", since = "4.2.0") private String ipAvailable; @SerializedName("volumelimit") - @Param(description = "the total volume which can be used by this project", since = "4.2.0") + @Param(description = "The total volume which can be used by this project", since = "4.2.0") private String volumeLimit; @SerializedName("volumetotal") - @Param(description = "the total volume being used by this project", since = "4.2.0") + @Param(description = "The total volume being used by this project", since = "4.2.0") private Long volumeTotal; @SerializedName("volumeavailable") - @Param(description = "the total volume available for this project", since = "4.2.0") + @Param(description = "The total volume available for this project", since = "4.2.0") private String volumeAvailable; @SerializedName("snapshotlimit") - @Param(description = "the total number of snapshots which can be stored by this project", since = "4.2.0") + @Param(description = "The total number of Snapshots which can be stored by this project", since = "4.2.0") private String snapshotLimit; @SerializedName("snapshottotal") - @Param(description = "the total number of snapshots stored by this project", since = "4.2.0") + @Param(description = "The total number of Snapshots stored by this project", since = "4.2.0") private Long snapshotTotal; @SerializedName("snapshotavailable") - @Param(description = "the total number of snapshots available for this project", since = "4.2.0") + @Param(description = "The total number of Snapshots available for this project", since = "4.2.0") private String snapshotAvailable; @SerializedName(ApiConstants.BACKUP_LIMIT) @@ -249,23 +249,23 @@ public class ProjectResponse extends BaseResponse implements ResourceLimitAndCou private String backupStorageAvailable; @SerializedName("templatelimit") - @Param(description = "the total number of templates which can be created by this project", since = "4.2.0") + @Param(description = "The total number of Templates which can be created by this project", since = "4.2.0") private String templateLimit; @SerializedName("templatetotal") - @Param(description = "the total number of templates which have been created by this project", since = "4.2.0") + @Param(description = "The total number of Templates which have been created by this project", since = "4.2.0") private Long templateTotal; @SerializedName("templateavailable") - @Param(description = "the total number of templates available to be created by this project", since = "4.2.0") + @Param(description = "The total number of Templates available to be created by this project", since = "4.2.0") private String templateAvailable; @SerializedName("vmstopped") - @Param(description = "the total number of virtual machines stopped for this project", since = "4.2.0") + @Param(description = "The total number of Instances stopped for this project", since = "4.2.0") private Integer vmStopped; @SerializedName("vmrunning") - @Param(description = "the total number of virtual machines running for this project", since = "4.2.0") + @Param(description = "The total number of Instances running for this project", since = "4.2.0") private Integer vmRunning; @SerializedName(ApiConstants.RESOURCE_ICON) @@ -273,7 +273,7 @@ public class ProjectResponse extends BaseResponse implements ResourceLimitAndCou ResourceIconResponse icon; @SerializedName(ApiConstants.CREATED) - @Param(description = "the date this project was created", since = "4.16.0") + @Param(description = "The date this project was created", since = "4.16.0") private Date created; @SerializedName(ApiConstants.TAGGED_RESOURCES) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ProjectRolePermissionResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ProjectRolePermissionResponse.java index 91b2036d3e61..55abd80c59b4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ProjectRolePermissionResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ProjectRolePermissionResponse.java @@ -27,19 +27,19 @@ @EntityReference(value = ProjectRolePermission.class) public class ProjectRolePermissionResponse extends BaseRolePermissionResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the project role permission") + @Param(description = "The ID of the project role permission") private String id; @SerializedName(ApiConstants.PROJECT_ROLE_ID) - @Param(description = "the ID of the project role to which the role permission belongs") + @Param(description = "The ID of the project role to which the role permission belongs") private String projectRoleId; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the ID of the project") + @Param(description = "The ID of the project") private String projectId; @SerializedName(ApiConstants.PROJECT_ROLE_NAME) - @Param(description = "the name of the project role to which the role permission belongs") + @Param(description = "The name of the project role to which the role permission belongs") private String projectRoleName; public String getId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ProjectRoleResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ProjectRoleResponse.java index 230329f0ee13..05df8d635ae6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ProjectRoleResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ProjectRoleResponse.java @@ -27,7 +27,7 @@ @EntityReference(value = ProjectRole.class) public class ProjectRoleResponse extends BaseRoleResponse { @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the id of the project") + @Param(description = "The ID of the project") private String projectId; public String getProjectId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ProviderResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ProviderResponse.java index 4d410837476e..7f08ee88a7df 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ProviderResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ProviderResponse.java @@ -31,31 +31,31 @@ public class ProviderResponse extends BaseResponse { @SerializedName(ApiConstants.NAME) - @Param(description = "the provider name") + @Param(description = "The provider name") private String name; @SerializedName(ApiConstants.PHYSICAL_NETWORK_ID) - @Param(description = "the physical network this belongs to") + @Param(description = "The physical Network this belongs to") private String physicalNetworkId; @SerializedName(ApiConstants.DEST_PHYSICAL_NETWORK_ID) - @Param(description = "the destination physical network") + @Param(description = "The destination physical Network") private String destinationPhysicalNetworkId; @SerializedName(ApiConstants.STATE) - @Param(description = "state of the network provider") + @Param(description = "State of the Network provider") private String state; @SerializedName(ApiConstants.ID) - @Param(description = "uuid of the network provider") + @Param(description = "UUID of the Network provider") private String id; @SerializedName(ApiConstants.SERVICE_LIST) - @Param(description = "services for this provider") + @Param(description = "Services for this provider") private List services; @SerializedName(ApiConstants.CAN_ENABLE_INDIVIDUAL_SERVICE) - @Param(description = "true if individual services can be enabled/disabled") + @Param(description = "True if individual services can be enabled/disabled") private Boolean canEnableIndividualServices; public void setName(String name) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/RegionResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/RegionResponse.java index 6c74fa623ce0..785bd1f6c238 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/RegionResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/RegionResponse.java @@ -28,23 +28,23 @@ @EntityReference(value = Region.class) public class RegionResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the region") + @Param(description = "The ID of the region") private Integer id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the region") + @Param(description = "The name of the region") private String name; @SerializedName(ApiConstants.END_POINT) - @Param(description = "the end point of the region") + @Param(description = "The end point of the region") private String endPoint; @SerializedName("gslbserviceenabled") - @Param(description = "true if GSLB service is enabled in the region, false otherwise") + @Param(description = "True if GSLB service is enabled in the region, false otherwise") private boolean gslbServiceEnabled; @SerializedName("portableipserviceenabled") - @Param(description = "true if security groups support is enabled, false otherwise") + @Param(description = "True if security groups support is enabled, false otherwise") private boolean portableipServiceEnabled; public Integer getId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/RegisterResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/RegisterUserKeyResponse.java similarity index 87% rename from api/src/main/java/org/apache/cloudstack/api/response/RegisterResponse.java rename to api/src/main/java/org/apache/cloudstack/api/response/RegisterUserKeyResponse.java index dd17cc5cc8af..4a5fb6b222e6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/RegisterResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/RegisterUserKeyResponse.java @@ -23,17 +23,17 @@ import com.cloud.serializer.Param; -public class RegisterResponse extends BaseResponse { +public class RegisterUserKeyResponse extends BaseResponse { @SerializedName(ApiConstants.API_KEY) - @Param(description = "the api key of the registered user", isSensitive = true) + @Param(description = "The API key of the registered user", isSensitive = true) private String apiKey; @SerializedName(ApiConstants.SECRET_KEY) - @Param(description = "the secret key of the registered user", isSensitive = true) + @Param(description = "The secret key of the registered user", isSensitive = true) private String secretKey; @SerializedName(ApiConstants.API_KEY_ACCESS) - @Param(description = "whether api key access is allowed or not", isSensitive = true) + @Param(description = "Whether API key access is allowed or not", isSensitive = true) private Boolean apiKeyAccess; public String getApiKey() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/RemoteAccessVpnResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/RemoteAccessVpnResponse.java index 54164e3ac34a..9fe5052b39d9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/RemoteAccessVpnResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/RemoteAccessVpnResponse.java @@ -30,55 +30,55 @@ public class RemoteAccessVpnResponse extends BaseResponse implements ControlledEntityResponse { @SerializedName(ApiConstants.PUBLIC_IP_ID) - @Param(description = "the public ip address of the vpn server") + @Param(description = "The public IP address of the VPN server") private String publicIpId; @SerializedName(ApiConstants.PUBLIC_IP) - @Param(description = "the public ip address of the vpn server") + @Param(description = "The public IP address of the VPN server") private String publicIp; @SerializedName("iprange") - @Param(description = "the range of ips to allocate to the clients") + @Param(description = "The range of IPs to allocate to the clients") private String ipRange; @SerializedName("presharedkey") - @Param(description = "the ipsec preshared key", isSensitive = true) + @Param(description = "The IPSec preshared key", isSensitive = true) private String presharedKey; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account of the remote access vpn") + @Param(description = "The Account of the remote access VPN") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the vpn") + @Param(description = "The project ID of the VPN") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the vpn") + @Param(description = "The project name of the VPN") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain id of the account of the remote access vpn") + @Param(description = "The domain ID of the Account of the remote access VPN") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name of the account of the remote access vpn") + @Param(description = "The domain name of the Account of the remote access VPN") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) - @Param(description = "path of the domain to which the remote access vpn belongs", since = "4.19.2.0") + @Param(description = "Path of the domain to which the remote access VPN belongs", since = "4.19.2.0") private String domainPath; @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the rule") + @Param(description = "The state of the rule") private String state; @SerializedName(ApiConstants.ID) - @Param(description = "the id of the remote access vpn") + @Param(description = "The ID of the remote access VPN") private String id; @SerializedName(ApiConstants.FOR_DISPLAY) - @Param(description = "is vpn for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) + @Param(description = "Is VPN for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; public void setPublicIp(String publicIp) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ResourceCountResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ResourceCountResponse.java index 74511a0f7430..d44e6c8fef5d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ResourceCountResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ResourceCountResponse.java @@ -26,35 +26,35 @@ @SuppressWarnings("unused") public class ResourceCountResponse extends BaseResponse implements ControlledEntityResponse { @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account for which resource count's are updated") + @Param(description = "The Account for which resource count's are updated") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id for which resource count's are updated") + @Param(description = "The project ID for which resource count's are updated") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name for which resource count's are updated") + @Param(description = "The project name for which resource count's are updated") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID for which resource count's are updated") + @Param(description = "The domain ID for which resource count's are updated") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name for which resource count's are updated") + @Param(description = "The domain name for which resource count's are updated") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) - @Param(description = "path of the domain to which the resource counts are updated", since = "4.19.2.0") + @Param(description = "Path of the domain to which the resource counts are updated", since = "4.19.2.0") private String domainPath; @SerializedName(ApiConstants.RESOURCE_TYPE) - @Param(description = "resource type. Values include 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11. See the resourceType parameter for more information on these values.") + @Param(description = "Resource type. Values include 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11. See the resourceType parameter for more information on these values.") private String resourceType; @SerializedName(ApiConstants.RESOURCE_TYPE_NAME) - @Param(description = "resource type name. Values include user_vm, public_ip, volume, snapshot, template, project, network, vpc, cpu, memory, primary_storage, secondary_storage.") + @Param(description = "Resource type name. Values include user_vm, public_ip, volume, Snapshot, Template, project, Network, VPC, CPU, memory, primary_storage, secondary_storage.") private String resourceTypeName; @SerializedName(ApiConstants.RESOURCE_COUNT) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ResourceDetailResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ResourceDetailResponse.java index 3ebb9c737ec4..bf5031a03ea4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ResourceDetailResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ResourceDetailResponse.java @@ -34,15 +34,15 @@ public class ResourceDetailResponse extends BaseResponse { private String resourceType; @SerializedName(ApiConstants.KEY) - @Param(description = "key of the resource detail") + @Param(description = "Key of the resource detail") private String name; @SerializedName(ApiConstants.VALUE) - @Param(description = "value of the resource detail") + @Param(description = "Value of the resource detail") private String value; @SerializedName(ApiConstants.FOR_DISPLAY) - @Param(description = "if detail is returned to the regular user", since = "4.3") + @Param(description = "If detail is returned to the regular user", since = "4.3") private boolean forDisplay; public String getResourceId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ResourceIconResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ResourceIconResponse.java index 403a91c88945..1b2055483887 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ResourceIconResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ResourceIconResponse.java @@ -24,15 +24,15 @@ public class ResourceIconResponse extends BaseResponse { @SerializedName(ApiConstants.RESOURCE_TYPE) - @Param(description = "resource type") + @Param(description = "Resource type") private ResourceTag.ResourceObjectType resourceType; @SerializedName(ApiConstants.RESOURCE_ID) - @Param(description = "id of the resource") + @Param(description = "ID of the resource") private String resourceId; @SerializedName(ApiConstants.BASE64_IMAGE) - @Param(description = "base64 representation of resource icon") + @Param(description = "Base64 representation of resource icon") private String image; public ResourceTag.ResourceObjectType getResourceType() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ResourceLimitResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ResourceLimitResponse.java index b5b03873f3f1..7b4a23444e5e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ResourceLimitResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ResourceLimitResponse.java @@ -29,39 +29,39 @@ @SuppressWarnings("unused") public class ResourceLimitResponse extends BaseResponse implements ControlledEntityResponse { @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account of the resource limit") + @Param(description = "The Account of the resource limit") private String accountName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID of the resource limit") + @Param(description = "The domain ID of the resource limit") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name of the resource limit") + @Param(description = "The domain name of the resource limit") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) - @Param(description = "path of the domain to which the resource limit belongs", since = "4.19.2.0") + @Param(description = "Path of the domain to which the resource limit belongs", since = "4.19.2.0") private String domainPath; @SerializedName(ApiConstants.RESOURCE_TYPE) - @Param(description = "resource type. Values include 0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11. See the resourceType parameter for more information on these values.") + @Param(description = "Resource type. Values include 0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11. See the resourceType parameter for more information on these values.") private String resourceType; @SerializedName(ApiConstants.RESOURCE_TYPE_NAME) - @Param(description = "resource type name. Values include user_vm, public_ip, volume, snapshot, template, project, network, vpc, cpu, memory, primary_storage, secondary_storage.") + @Param(description = "Resource type name. Values include user_vm, public_ip, volume, Snapshot, Template, project, Network, VPC, CPU, memory, primary_storage, secondary_storage.") private String resourceTypeName; @SerializedName("max") - @Param(description = "the maximum number of the resource. A -1 means the resource currently has no limit.") + @Param(description = "The maximum number of the resource. A -1 means the resource currently has no limit.") private Long max; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the resource limit") + @Param(description = "The project ID of the resource limit") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the resource limit") + @Param(description = "The project name of the resource limit") private String projectName; @SerializedName(ApiConstants.TAG) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ResourceScheduleResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ResourceScheduleResponse.java new file mode 100644 index 000000000000..ff799b467cff --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ResourceScheduleResponse.java @@ -0,0 +1,178 @@ +/* + * 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.Map; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.schedule.ResourceSchedule; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = ResourceSchedule.class) +public class ResourceScheduleResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "The ID of resource schedule") + private String id; + + @SerializedName(ApiConstants.RESOURCE_TYPE) + @Param(description = "Type of the resource") + private ApiCommandResourceType resourceType; + + @SerializedName(ApiConstants.RESOURCE_ID) + @Param(description = "ID of the resource") + private String resourceId; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "Description of resource schedule") + private String description; + + @SerializedName(ApiConstants.SCHEDULE) + @Param(description = "Cron formatted resource schedule") + private String schedule; + + @SerializedName(ApiConstants.TIMEZONE) + @Param(description = "Timezone of the schedule") + private String timeZone; + + @SerializedName(ApiConstants.ACTION) + @Param(description = "Action") + private ResourceSchedule.Action action; + + @SerializedName(ApiConstants.ENABLED) + @Param(description = "Resource schedule is enabled") + private boolean enabled; + + @SerializedName(ApiConstants.START_DATE) + @Param(description = "Date from which the schedule is active") + private Date startDate; + + @SerializedName(ApiConstants.END_DATE) + @Param(description = "Date after which the schedule becomes inactive") + private Date endDate; + + @SerializedName(ApiConstants.DETAILS) + @Param(description = "Schedule details") + private Map details; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "Date when the schedule was created") + private Date created; + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setResourceType(ApiCommandResourceType resourceType) { + this.resourceType = resourceType; + } + + public ApiCommandResourceType getResourceType() { + return resourceType; + } + + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + public String getResourceId() { + return resourceId; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + + public void setSchedule(String schedule) { + this.schedule = schedule; + } + + public String getSchedule() { + return schedule; + } + + public void setTimeZone(String timeZone) { + this.timeZone = timeZone; + } + + public String getTimeZone() { + return timeZone; + } + + public void setAction(ResourceSchedule.Action action) { + this.action = action; + } + + public ResourceSchedule.Action getAction() { + return action; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean getEnabled() { + return enabled; + } + + public void setStartDate(Date startDate) { + this.startDate = startDate; + } + + public Date getStartDate() { + return startDate; + } + + public void setEndDate(Date endDate) { + this.endDate = endDate; + } + + public Date getEndDate() { + return endDate; + } + + public void setDetails(Map details) { + this.details = details; + } + + public Map getDetails() { + return details; + } + + public void setCreated(Date created) { + this.created = created; + } + + public Date getCreated() { + return created; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ResourceTagResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ResourceTagResponse.java index 26e4d19cdba7..af90d68dd66c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ResourceTagResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ResourceTagResponse.java @@ -26,47 +26,47 @@ @SuppressWarnings("unused") public class ResourceTagResponse extends BaseResponse implements ControlledViewEntityResponse { @SerializedName(ApiConstants.KEY) - @Param(description = "tag key name") + @Param(description = "Tag key name") private String key; @SerializedName(ApiConstants.VALUE) - @Param(description = "tag value") + @Param(description = "Tag value") private String value; @SerializedName(ApiConstants.RESOURCE_TYPE) - @Param(description = "resource type") + @Param(description = "Resource type") private String resourceType; @SerializedName(ApiConstants.RESOURCE_ID) - @Param(description = "id of the resource") + @Param(description = "ID of the resource") private String resourceId; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account associated with the tag") + @Param(description = "The Account associated with the tag") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id the tag belongs to") + @Param(description = "The project ID the tag belongs to") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name where tag belongs to") + @Param(description = "The project name where tag belongs to") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the ID of the domain associated with the tag") + @Param(description = "The ID of the domain associated with the tag") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain associated with the tag") + @Param(description = "The domain associated with the tag") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) - @Param(description = "path of the Domain associated with the tag", since = "4.19.2.0") + @Param(description = "Path of the Domain associated with the tag", since = "4.19.2.0") private String domainPath; @SerializedName(ApiConstants.CUSTOMER) - @Param(description = "customer associated with the tag") + @Param(description = "Customer associated with the tag") private String customer; public void setKey(String key) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/RolePermissionResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/RolePermissionResponse.java index 7bff8764cbe7..6b47cae45734 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/RolePermissionResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/RolePermissionResponse.java @@ -27,15 +27,15 @@ @EntityReference(value = RolePermission.class) public class RolePermissionResponse extends BaseRolePermissionResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the role permission") + @Param(description = "The ID of the role permission") private String id; @SerializedName(ApiConstants.ROLE_ID) - @Param(description = "the ID of the role to which the role permission belongs") + @Param(description = "The ID of the role to which the role permission belongs") private String roleId; @SerializedName(ApiConstants.ROLE_NAME) - @Param(description = "the name of the role to which the role permission belongs") + @Param(description = "The name of the role to which the role permission belongs") private String roleName; public String getId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/RoleResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/RoleResponse.java index 92e3b46139f3..b394e83dca53 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/RoleResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/RoleResponse.java @@ -29,11 +29,11 @@ public class RoleResponse extends BaseRoleResponse { @SerializedName(ApiConstants.TYPE) - @Param(description = "the type of the role") + @Param(description = "The type of the role") private String roleType; @SerializedName(ApiConstants.IS_DEFAULT) - @Param(description = "true if role is default, false otherwise") + @Param(description = "True if role is default, false otherwise") private Boolean isDefault; @SerializedName(ApiConstants.STATE) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/RollingMaintenanceHostSkippedResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/RollingMaintenanceHostSkippedResponse.java index 8d304543fb97..0071498f4270 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/RollingMaintenanceHostSkippedResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/RollingMaintenanceHostSkippedResponse.java @@ -24,15 +24,15 @@ public class RollingMaintenanceHostSkippedResponse extends BaseResponse { @SerializedName(ApiConstants.HOST_ID) - @Param(description = "the ID of the skipped host") + @Param(description = "The ID of the skipped host") private String hostId; @SerializedName(ApiConstants.HOST_NAME) - @Param(description = "the name of the skipped host") + @Param(description = "The name of the skipped host") private String hostName; @SerializedName(ApiConstants.ACL_REASON) - @Param(description = "the reason to skip the host") + @Param(description = "The reason to skip the host") private String reason; public String getHostId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/RollingMaintenanceHostUpdatedResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/RollingMaintenanceHostUpdatedResponse.java index 821257d4e076..a1670831af23 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/RollingMaintenanceHostUpdatedResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/RollingMaintenanceHostUpdatedResponse.java @@ -24,23 +24,23 @@ public class RollingMaintenanceHostUpdatedResponse extends BaseResponse { @SerializedName(ApiConstants.HOST_ID) - @Param(description = "the ID of the updated host") + @Param(description = "The ID of the updated host") private String hostId; @SerializedName(ApiConstants.HOST_NAME) - @Param(description = "the name of the updated host") + @Param(description = "The name of the updated host") private String hostName; @SerializedName(ApiConstants.START_DATE) - @Param(description = "start date of the update on the host") + @Param(description = "Start date of the update on the host") private String startDate; @SerializedName(ApiConstants.END_DATE) - @Param(description = "end date of the update on the host") + @Param(description = "End date of the update on the host") private String endDate; @SerializedName(ApiConstants.OUTPUT) - @Param(description = "output of the maintenance script on the host") + @Param(description = "Output of the maintenance script on the host") private String output; public String getHostId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/RollingMaintenanceResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/RollingMaintenanceResponse.java index 89b838c2843f..56427d7eaca9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/RollingMaintenanceResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/RollingMaintenanceResponse.java @@ -25,19 +25,19 @@ public class RollingMaintenanceResponse extends BaseResponse { @SerializedName("success") - @Param(description = "indicates if the rolling maintenance operation was successful") + @Param(description = "Indicates if the rolling maintenance operation was successful") private Boolean success; @SerializedName("details") - @Param(description = "in case of failure, details are displayed") + @Param(description = "In case of failure, details are displayed") private String details; @SerializedName("hostsupdated") - @Param(description = "the hosts updated", responseObject = RollingMaintenanceHostUpdatedResponse.class) + @Param(description = "The hosts updated", responseObject = RollingMaintenanceHostUpdatedResponse.class) private List updatedHosts; @SerializedName("hostsskipped") - @Param(description = "the hosts skipped", responseObject = RollingMaintenanceHostSkippedResponse.class) + @Param(description = "The hosts skipped", responseObject = RollingMaintenanceHostSkippedResponse.class) private List skippedHosts; public RollingMaintenanceResponse(Boolean success, String details) { 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 00f1e4e3bb0b..9716c564634e 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 @@ -19,6 +19,7 @@ import java.util.Date; +import com.cloud.network.VirtualNetworkApplianceService.RouterHealthStatus; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; @@ -27,23 +28,27 @@ public class RouterHealthCheckResultResponse extends BaseResponse { @SerializedName(ApiConstants.ROUTER_CHECK_NAME) - @Param(description = "the name of the health check on the router") + @Param(description = "The name of the health check on the router") private String checkName; @SerializedName(ApiConstants.ROUTER_CHECK_TYPE) - @Param(description = "the type of the health check - basic or advanced") + @Param(description = "The type of the health check - basic or advanced") private String checkType; @SerializedName(ApiConstants.SUCCESS) - @Param(description = "result of the health check") + @Param(description = "The result of the health check if available") private boolean result; + @SerializedName(ApiConstants.STATUS) + @Param(description = "the result of the health check in enum form: {SUCCESS, FAILURE, WARNING, UNKNOWN}") + private RouterHealthStatus state; + @SerializedName(ApiConstants.LAST_UPDATED) - @Param(description = "the date this VPC was created") + @Param(description = "The date this VPC was created") private Date lastUpdated; @SerializedName(ApiConstants.DETAILS) - @Param(description = "detailed response generated on running health check") + @Param(description = "Detailed response generated on running health check") private String details; public String getCheckName() { @@ -54,10 +59,14 @@ public String getCheckType() { return checkType; } - public boolean getResult() { + public Boolean getResult() { return result; } + public RouterHealthStatus getState() { + return state; + } + public Date getLastUpdated() { return lastUpdated; } @@ -74,10 +83,14 @@ public void setCheckType(String checkType) { this.checkType = checkType; } - public void setResult(boolean result) { + public void setResult(Boolean result) { this.result = result; } + public void setState(RouterHealthStatus state) { + this.state = state; + } + public void setLastUpdated(Date lastUpdated) { this.lastUpdated = lastUpdated; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/RouterHealthCheckResultsListResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/RouterHealthCheckResultsListResponse.java index e56f70d2c59c..0d2d6cf88825 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/RouterHealthCheckResultsListResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/RouterHealthCheckResultsListResponse.java @@ -27,11 +27,11 @@ public class RouterHealthCheckResultsListResponse extends BaseResponse { @SerializedName(ApiConstants.ROUTER_ID) - @Param(description = "the id of the router") + @Param(description = "The ID of the router") private String routerId; @SerializedName(ApiConstants.ROUTER_HEALTH_CHECKS) - @Param(description = "the id of the router") + @Param(description = "The ID of the router") private List healthChecks; public String getRouterId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/RunDiagnosticsResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/RunDiagnosticsResponse.java index 4c8a923613ab..0583020c7da8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/RunDiagnosticsResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/RunDiagnosticsResponse.java @@ -29,15 +29,15 @@ @EntityReference(value = VirtualMachine.class) public class RunDiagnosticsResponse extends BaseResponse { @SerializedName(ApiConstants.STDOUT) - @Param(description = "the standard output from the command execution") + @Param(description = "The standard output from the command execution") private String stdout; @SerializedName(ApiConstants.STDERR) - @Param(description = "the standard error output from the command execution") + @Param(description = "The standard error output from the command execution") private String stderr; @SerializedName(ApiConstants.EXITCODE) - @Param(description = "the command execution return code") + @Param(description = "The command execution return code") private String exitCode; public String getStdout() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/SSHKeyPairResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SSHKeyPairResponse.java index 65e5c5b74131..a2b9788f9f04 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/SSHKeyPairResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/SSHKeyPairResponse.java @@ -30,28 +30,28 @@ public class SSHKeyPairResponse extends BaseResponseWithAnnotations { @SerializedName(ApiConstants.ID) - @Param(description = "ID of the ssh keypair") + @Param(description = "ID of the SSH keypair") private String id; @SerializedName(ApiConstants.NAME) @Param(description = "Name of the keypair") private String name; - @SerializedName(ApiConstants.ACCOUNT) @Param(description="the owner of the keypair") + @SerializedName(ApiConstants.ACCOUNT) @Param(description = "The owner of the keypair") private String accountName; - @SerializedName(ApiConstants.DOMAIN_ID) @Param(description="the domain id of the keypair owner") + @SerializedName(ApiConstants.DOMAIN_ID) @Param(description = "The domain id of the keypair owner") private String domainId; - @SerializedName(ApiConstants.DOMAIN) @Param(description="the domain name of the keypair owner") + @SerializedName(ApiConstants.DOMAIN) @Param(description = "The domain name of the keypair owner") private String domain; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the keypair owner") + @Param(description = "The project id of the keypair owner") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the keypair owner") + @Param(description = "The project name of the keypair owner") private String projectName; @SerializedName("fingerprint") diff --git a/api/src/main/java/org/apache/cloudstack/api/response/SecurityGroupResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SecurityGroupResponse.java index de486b5a3746..2085e6089737 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/SecurityGroupResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/SecurityGroupResponse.java @@ -32,35 +32,35 @@ public class SecurityGroupResponse extends BaseResponse implements ControlledViewEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the security group") + @Param(description = "The ID of the security group") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the security group") + @Param(description = "The name of the security group") private String name; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "the description of the security group") + @Param(description = "The description of the security group") private String description; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account owning the security group") + @Param(description = "The Account owning the security group") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the group") + @Param(description = "The project id of the group") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the group") + @Param(description = "The project name of the group") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID of the security group") + @Param(description = "The domain ID of the security group") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name of the security group") + @Param(description = "The domain name of the security group") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) @@ -68,23 +68,23 @@ public class SecurityGroupResponse extends BaseResponse implements ControlledVie private String domainPath; @SerializedName("ingressrule") - @Param(description = "the list of ingress rules associated with the security group", responseObject = SecurityGroupRuleResponse.class) + @Param(description = "The list of ingress rules associated with the security group", responseObject = SecurityGroupRuleResponse.class) private Set ingressRules; @SerializedName("egressrule") - @Param(description = "the list of egress rules associated with the security group", responseObject = SecurityGroupRuleResponse.class) + @Param(description = "The list of egress rules associated with the security group", responseObject = SecurityGroupRuleResponse.class) private Set egressRules; @SerializedName(ApiConstants.TAGS) - @Param(description = "the list of resource tags associated with the rule", responseObject = ResourceTagResponse.class) + @Param(description = "The list of resource tags associated with the rule", responseObject = ResourceTagResponse.class) private Set tags; @SerializedName(ApiConstants.VIRTUAL_MACHINE_COUNT) - @Param(description = "the number of virtualmachines associated with this securitygroup", since = "4.6.0") + @Param(description = "The number of Instances associated with this Security Group", since = "4.6.0") private Integer virtualMachineCount; @SerializedName(ApiConstants.VIRTUAL_MACHINE_IDS) - @Param(description = "the list of virtualmachine ids associated with this securitygroup", since = "4.6.0") + @Param(description = "The list of Instance IDs associated with this Security Group", since = "4.6.0") private Set virtualMachineIds; public SecurityGroupResponse() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/SecurityGroupRuleResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SecurityGroupRuleResponse.java index 9e553aa32ccb..734fa50acb16 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/SecurityGroupRuleResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/SecurityGroupRuleResponse.java @@ -30,43 +30,43 @@ @EntityReference(value = SecurityRule.class) public class SecurityGroupRuleResponse extends BaseResponse { @SerializedName("ruleid") - @Param(description = "the id of the security group rule") + @Param(description = "The ID of the security group rule") private String ruleId; @SerializedName("protocol") - @Param(description = "the protocol of the security group rule") + @Param(description = "The protocol of the security group rule") private String protocol; @SerializedName(ApiConstants.ICMP_TYPE) - @Param(description = "the type of the ICMP message response") + @Param(description = "The type of the ICMP message response") private Integer icmpType; @SerializedName(ApiConstants.ICMP_CODE) - @Param(description = "the code for the ICMP message response") + @Param(description = "The code for the ICMP message response") private Integer icmpCode; @SerializedName(ApiConstants.START_PORT) - @Param(description = "the starting IP of the security group rule") + @Param(description = "The starting IP of the security group rule") private Integer startPort; @SerializedName(ApiConstants.END_PORT) - @Param(description = "the ending IP of the security group rule ") + @Param(description = "The ending IP of the security group rule ") private Integer endPort; @SerializedName(ApiConstants.SECURITY_GROUP_NAME) - @Param(description = "security group name") + @Param(description = "Security group name") private String securityGroupName; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "account owning the security group rule") + @Param(description = "Account owning the security group rule") private String accountName; @SerializedName(ApiConstants.CIDR) - @Param(description = "the CIDR notation for the base IP address of the security group rule") + @Param(description = "The CIDR notation for the base IP address of the security group rule") private String cidr; @SerializedName(ApiConstants.TAGS) - @Param(description = "the list of resource tags associated with the rule", responseObject = ResourceTagResponse.class) + @Param(description = "The list of resource tags associated with the rule", responseObject = ResourceTagResponse.class) private java.util.Set tags; public String getRuleId() { 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 4565a878b348..99c50829f048 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 @@ -30,178 +30,178 @@ @EntityReference(value = ServiceOffering.class) public class ServiceOfferingResponse extends BaseResponseWithAnnotations { @SerializedName("id") - @Param(description = "the id of the service offering") + @Param(description = "The ID of the service offering") private String id; @SerializedName("name") - @Param(description = "the name of the service offering") + @Param(description = "The name of the service offering") private String name; @SerializedName("state") - @Param(description = "state of the service offering") + @Param(description = "State of the service offering") private String state; @SerializedName("displaytext") - @Param(description = "an alternate display text of the service offering.") + @Param(description = "An alternate display text of the service offering.") private String displayText; @SerializedName("cpunumber") - @Param(description = "the number of CPU") + @Param(description = "The number of CPU") private Integer cpuNumber; @SerializedName("cpuspeed") - @Param(description = "the clock rate CPU speed in Mhz") + @Param(description = "The clock rate CPU speed in Mhz") private Integer cpuSpeed; @SerializedName("memory") - @Param(description = "the memory in MB") + @Param(description = "The memory in MB") private Integer memory; @SerializedName("created") - @Param(description = "the date this service offering was created") + @Param(description = "The date this service offering was created") private Date created; @SerializedName("storagetype") - @Param(description = "the storage type for this service offering") + @Param(description = "The storage type for this service offering") private String storageType; - @SerializedName("provisioningtype") @Param(description="provisioning type used to create volumes. Valid values are thin, sparse, fat.", since = "4.4.0") + @SerializedName("provisioningtype") @Param(description = "Provisioning type used to create volumes. Valid values are thin, sparse, fat.", since = "4.4.0") private String provisioningType; @SerializedName("offerha") - @Param(description = "the ha support in the service offering") + @Param(description = "The HA support in the service offering") private Boolean offerHa; @SerializedName("limitcpuuse") - @Param(description = "restrict the CPU usage to committed service offering") + @Param(description = "Restrict the CPU usage to committed service offering") private Boolean limitCpuUse; @SerializedName("isvolatile") - @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") + @Param(description = "True if the Instance needs to be volatile, i.e., on every reboot of Instance from API root disk is discarded and creates a new root disk") private Boolean isVolatile; @SerializedName(ApiConstants.STORAGE_TAGS) - @Param(description = "the tags for the service offering") + @Param(description = "The tags for the service offering") private String tags; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID(s) this disk offering belongs to. Ignore this information as it is not currently applicable.") + @Param(description = "The domain ID(s) this disk offering belongs to. Ignore this information as it is not currently applicable.") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name(s) this disk offering belongs to. Ignore this information as it is not currently applicable.") + @Param(description = "The domain name(s) this disk offering belongs to. Ignore this information as it is not currently applicable.") private String domain; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the zone ID(s) this disk offering belongs to. Ignore this information as it is not currently applicable.", since = "4.13.0") + @Param(description = "The zone ID(s) this disk offering belongs to. Ignore this information as it is not currently applicable.", since = "4.13.0") private String zoneId; @SerializedName(ApiConstants.ZONE) - @Param(description = "the zone name(s) this disk offering belongs to. Ignore this information as it is not currently applicable.", since = "4.13.0") + @Param(description = "The zone name(s) this disk offering belongs to. Ignore this information as it is not currently applicable.", since = "4.13.0") private String zone; @SerializedName(ApiConstants.HOST_TAGS) - @Param(description = "the host tag for the service offering") + @Param(description = "The host tag for the service offering") private String hostTag; @SerializedName(ApiConstants.IS_SYSTEM_OFFERING) - @Param(description = "is this a system vm offering") + @Param(description = "Is this a System VM offering") private Boolean isSystem; @SerializedName(ApiConstants.IS_DEFAULT_USE) - @Param(description = "is this a default system vm offering") + @Param(description = "Is this a default System VM offering") private Boolean defaultUse; @SerializedName(ApiConstants.SYSTEM_VM_TYPE) - @Param(description = "is this a the systemvm type for system vm offering") + @Param(description = "Is this a the System VM type for System VM offering") private String vmType; @SerializedName(ApiConstants.NETWORKRATE) - @Param(description = "data transfer rate in megabits per second allowed.") + @Param(description = "Data transfer rate in megabits per second allowed.") private Integer networkRate; @SerializedName("iscustomizediops") - @Param(description = "true if disk offering uses custom iops, false otherwise", since = "4.4") + @Param(description = "True if disk offering uses custom IOPS, false otherwise", since = "4.4") private Boolean customizedIops; @SerializedName(ApiConstants.MIN_IOPS) - @Param(description = "the min iops of the disk offering", since = "4.4") + @Param(description = "The min IOPS of the disk offering", since = "4.4") private Long minIops; @SerializedName(ApiConstants.MAX_IOPS) - @Param(description = "the max iops of the disk offering", since = "4.4") + @Param(description = "The max IOPS of the disk offering", since = "4.4") private Long maxIops; @SerializedName(ApiConstants.HYPERVISOR_SNAPSHOT_RESERVE) - @Param(description = "Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)", since = "4.4") + @Param(description = "Hypervisor Snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)", since = "4.4") private Integer hypervisorSnapshotReserve; @SerializedName("diskBytesReadRate") - @Param(description = "bytes read rate of the service offering") + @Param(description = "Bytes read rate of the service offering") private Long bytesReadRate; @SerializedName("diskBytesReadRateMax") - @Param(description = "burst bytes read rate of the disk offering") + @Param(description = "Burst bytes read rate of the disk offering") private Long bytesReadRateMax; @SerializedName("diskBytesReadRateMaxLength") - @Param(description = "length (in seconds) of the burst") + @Param(description = "Length (in seconds) of the burst") private Long bytesReadRateMaxLength; @SerializedName("diskBytesWriteRate") - @Param(description = "bytes write rate of the service offering") + @Param(description = "Bytes write rate of the service offering") private Long bytesWriteRate; @SerializedName("diskBytesWriteRateMax") - @Param(description = "burst bytes write rate of the disk offering") + @Param(description = "Burst bytes write rate of the disk offering") private Long bytesWriteRateMax; @SerializedName("diskBytesWriteRateMaxLength") - @Param(description = "length (in seconds) of the burst") + @Param(description = "Length (in seconds) of the burst") private Long bytesWriteRateMaxLength; @SerializedName("diskIopsReadRate") - @Param(description = "io requests read rate of the service offering") + @Param(description = "I/O requests read rate of the service offering") private Long iopsReadRate; @SerializedName("diskIopsReadRateMax") - @Param(description = "burst io requests read rate of the disk offering") + @Param(description = "Burst io requests read rate of the disk offering") private Long iopsReadRateMax; @SerializedName("diskIopsReadRateMaxLength") - @Param(description = "length (in second) of the burst") + @Param(description = "Length (in second) of the burst") private Long iopsReadRateMaxLength; @SerializedName("diskIopsWriteRate") - @Param(description = "io requests write rate of the service offering") + @Param(description = "I/O requests write rate of the service offering") private Long iopsWriteRate; @SerializedName("diskIopsWriteRateMax") - @Param(description = "burst io requests write rate of the disk offering") + @Param(description = "Burst I/O requests write rate of the disk offering") private Long iopsWriteRateMax; @SerializedName("diskIopsWriteRateMaxLength") - @Param(description = "length (in seconds) of the burst") + @Param(description = "Length (in seconds) of the burst") private Long iopsWriteRateMaxLength; @SerializedName(ApiConstants.DEPLOYMENT_PLANNER) - @Param(description = "deployment strategy used to deploy VM.") + @Param(description = "Deployment strategy used to deploy Instance.") private String deploymentPlanner; @SerializedName(ApiConstants.SERVICE_OFFERING_DETAILS) - @Param(description = "additional key/value details tied with this service offering", since = "4.2.0") + @Param(description = "Additional key/value details tied with this service offering", since = "4.2.0") private Map details; @SerializedName("iscustomized") - @Param(description = "is true if the offering is customized", since = "4.3.0") + @Param(description = "Is true if the offering is customized", since = "4.3.0") private Boolean isCustomized; @SerializedName("cacheMode") - @Param(description = "the cache mode to use for this disk offering. none, writeback or writethrough", since = "4.14") + @Param(description = "The cache mode to use for this disk offering. none, writeback, writethrough or hypervisor default", since = "4.14") private String cacheMode; @SerializedName("vspherestoragepolicy") - @Param(description = "the vsphere storage policy tagged to the service offering in case of VMware", since = "4.15") + @Param(description = "The vsphere storage policy tagged to the service offering in case of VMware", since = "4.15") private String vsphereStoragePolicy; @SerializedName(ApiConstants.ROOT_DISK_SIZE) @@ -209,29 +209,29 @@ public class ServiceOfferingResponse extends BaseResponseWithAnnotations { private Long rootDiskSize; @SerializedName(ApiConstants.DYNAMIC_SCALING_ENABLED) - @Param(description = "true if virtual machine needs to be dynamically scalable of cpu or memory", since = "4.16") + @Param(description = "True if Instance needs to be dynamically scalable of CPU or memory", since = "4.16") private Boolean dynamicScalingEnabled; @SerializedName(ApiConstants.DISK_OFFERING_STRICTNESS) @Param(description = "True/False to indicate the strictness of the disk offering association with the compute offering. " + - "When set to true, override of disk offering is not allowed when VM is deployed and " + - "change disk offering is not allowed for the ROOT disk after the VM is deployed", since = "4.17") + "When set to true, override of disk offering is not allowed when Instance is deployed and " + + "change disk offering is not allowed for the ROOT disk after the Instance is deployed", since = "4.17") private Boolean diskOfferingStrictness; @SerializedName(ApiConstants.DISK_OFFERING_ID) - @Param(description = "the ID of the disk offering to which service offering is linked", since = "4.17") + @Param(description = "The ID of the disk offering to which service offering is linked", since = "4.17") private String diskOfferingId; @SerializedName("diskofferingname") - @Param(description = "name of the disk offering", since = "4.17") + @Param(description = "Name of the disk offering", since = "4.17") private String diskOfferingName; @SerializedName("diskofferingdisplaytext") - @Param(description = "the display text of the disk offering", since = "4.17") + @Param(description = "The display text of the disk offering", since = "4.17") private String diskOfferingDisplayText; @SerializedName(ApiConstants.ENCRYPT_ROOT) - @Param(description = "true if virtual machine root disk will be encrypted on storage", since = "4.18") + @Param(description = "True if Instance root disk will be encrypted on storage", since = "4.18") private Boolean encryptRoot; @SerializedName(ApiConstants.GPU_CARD_ID) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ServiceResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ServiceResponse.java index 9f5acc13f8bb..9d1470b64d70 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ServiceResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ServiceResponse.java @@ -29,15 +29,15 @@ public class ServiceResponse extends BaseResponse { @SerializedName(ApiConstants.NAME) - @Param(description = "the service name") + @Param(description = "The service name") private String name; @SerializedName(ApiConstants.PROVIDER) - @Param(description = "the service provider name", responseObject = ProviderResponse.class) + @Param(description = "The service provider name", responseObject = ProviderResponse.class) private List providers; @SerializedName("capability") - @Param(description = "the list of capabilities", responseObject = CapabilityResponse.class) + @Param(description = "The list of capabilities", responseObject = CapabilityResponse.class) private List capabilities; public void setName(String name) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteCustomerGatewayResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteCustomerGatewayResponse.java index 4ae140ec573d..b121ef7ce61e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteCustomerGatewayResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteCustomerGatewayResponse.java @@ -31,23 +31,23 @@ @SuppressWarnings("unused") public class Site2SiteCustomerGatewayResponse extends BaseResponseWithAnnotations implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the vpn gateway ID") + @Param(description = "The VPN gateway ID") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "name of the customer gateway") + @Param(description = "Name of the customer gateway") private String name; @SerializedName(ApiConstants.GATEWAY) - @Param(description = "public ip address id of the customer gateway") + @Param(description = "Public IP address ID of the customer gateway") private String gatewayIp; @SerializedName(ApiConstants.IP_ADDRESS) - @Param(description = "guest ip of the customer gateway") + @Param(description = "Guest IP of the customer gateway") private String guestIp; @SerializedName(ApiConstants.CIDR_LIST) - @Param(description = "guest cidr list of the customer gateway. Multiple entries are separated by a single comma character (,).") + @Param(description = "Guest CIDR list of the customer gateway. Multiple entries are separated by a single comma character (,).") private String guestCidrList; @SerializedName(ApiConstants.IPSEC_PSK) @@ -71,49 +71,57 @@ public class Site2SiteCustomerGatewayResponse extends BaseResponseWithAnnotation private Long espLifetime; @SerializedName(ApiConstants.DPD) - @Param(description = "if DPD is enabled for customer gateway") + @Param(description = "If DPD is enabled for customer gateway") private Boolean dpd; @SerializedName(ApiConstants.FORCE_ENCAP) - @Param(description = "if Force NAT Encapsulation is enabled for customer gateway") + @Param(description = "If Force NAT Encapsulation is enabled for customer gateway") private Boolean encap; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the owner") + @Param(description = "The owner") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id") + @Param(description = "The project ID") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name") + @Param(description = "The project name") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain id of the owner") + @Param(description = "The domain ID of the owner") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name of the owner") + @Param(description = "The domain name of the owner") private String domain; @SerializedName(ApiConstants.DOMAIN_PATH) - @Param(description = "the domain path of the owner", since = "4.19.2.0") + @Param(description = "The domain path of the owner", since = "4.19.2.0") private String domainPath; @SerializedName(ApiConstants.REMOVED) - @Param(description = "the date and time the host was removed") + @Param(description = "The date and time the host was removed") private Date removed; @SerializedName(ApiConstants.SPLIT_CONNECTIONS) - @Param(description = "For IKEv2, whether to split multiple right subnet cidrs into multiple connection statements.") + @Param(description = "For IKEv2, whether to split multiple right subnet CIDRs into multiple connection statements.") private Boolean splitConnections; @SerializedName(ApiConstants.IKE_VERSION) - @Param(description = "Which IKE Version to use, one of ike (autoselect), ikev1, or ikev2. Defaults to ike") + @Param(description = "Which IKE Version to use, one of ike (autoselect), IKEv1, or IKEv2. Defaults to ike") private String ikeVersion; + @SerializedName(ApiConstants.OBSOLETE_PARAMETERS) + @Param(description = "Contains the list of obsolete/insecure cryptographic parameters that the vpn customer gateway is using.", since = "4.23.0") + private String obsoleteParameters; + + @SerializedName(ApiConstants.EXCLUDED_PARAMETERS) + @Param(description = "Contains the list of excluded/not allowed cryptographic parameters that the vpn customer gateway is using.", since = "4.23.0") + private String excludedParameters; + public void setId(String id) { this.id = id; } @@ -202,4 +210,12 @@ public void setDomainPath(String domainPath) { this.domainPath = domainPath; } + public void setContainsObsoleteParameters(String obsoleteParameters) { + this.obsoleteParameters = obsoleteParameters; + } + + public void setContainsExcludedParameters(String excludedParameters) { + this.excludedParameters = excludedParameters; + } + } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteVpnConnectionResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteVpnConnectionResponse.java index a9fd0f9703cc..e668b7d948fd 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteVpnConnectionResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteVpnConnectionResponse.java @@ -31,29 +31,29 @@ @SuppressWarnings("unused") public class Site2SiteVpnConnectionResponse extends BaseResponse implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the connection ID") + @Param(description = "The connection ID") private String id; @SerializedName(ApiConstants.S2S_VPN_GATEWAY_ID) - @Param(description = "the vpn gateway ID") + @Param(description = "The VPN gateway ID") private String vpnGatewayId; @SerializedName(ApiConstants.PUBLIC_IP) - @Param(description = "the public IP address") + @Param(description = "The public IP address") //from VpnGateway private String ip; @SerializedName(ApiConstants.S2S_CUSTOMER_GATEWAY_ID) - @Param(description = "the customer gateway ID") + @Param(description = "The customer gateway ID") private String customerGatewayId; @SerializedName(ApiConstants.GATEWAY) - @Param(description = "public ip address id of the customer gateway") + @Param(description = "Public IP address id of the customer gateway") //from CustomerGateway private String gatewayIp; @SerializedName(ApiConstants.CIDR_LIST) - @Param(description = "guest cidr list of the customer gateway. Multiple entries are separated by a single comma character (,).") + @Param(description = "Guest CIDR list of the customer gateway. Multiple entries are separated by a single comma character (,).") //from CustomerGateway private String guestCidrList; @@ -83,41 +83,41 @@ public class Site2SiteVpnConnectionResponse extends BaseResponse implements Cont private Long espLifetime; @SerializedName(ApiConstants.DPD) - @Param(description = "if DPD is enabled for customer gateway") + @Param(description = "If DPD is enabled for customer gateway") //from CustomerGateway private Boolean dpd; @SerializedName(ApiConstants.FORCE_ENCAP) - @Param(description = "if Force NAT Encapsulation is enabled for customer gateway") + @Param(description = "If Force NAT Encapsulation is enabled for customer gateway") //from CustomerGateway private Boolean encap; @SerializedName(ApiConstants.STATE) - @Param(description = "State of vpn connection") + @Param(description = "State of VPN connection") private String state; @SerializedName(ApiConstants.PASSIVE) - @Param(description = "State of vpn connection") + @Param(description = "State of VPN connection") private boolean passive; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the owner") + @Param(description = "The owner") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id") + @Param(description = "The project id") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name") + @Param(description = "The project name") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain id of the owner") + @Param(description = "The domain id of the owner") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name of the owner") + @Param(description = "The domain name of the owner") private String domain; @SerializedName(ApiConstants.DOMAIN_PATH) @@ -125,23 +125,23 @@ public class Site2SiteVpnConnectionResponse extends BaseResponse implements Cont private String domainPath; @SerializedName(ApiConstants.CREATED) - @Param(description = "the date and time the host was created") + @Param(description = "The date and time the host was created") private Date created; @SerializedName(ApiConstants.REMOVED) - @Param(description = "the date and time the host was removed") + @Param(description = "The date and time the host was removed") private Date removed; @SerializedName(ApiConstants.FOR_DISPLAY) - @Param(description = "is connection for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) + @Param(description = "Is connection for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; @SerializedName(ApiConstants.SPLIT_CONNECTIONS) - @Param(description = "Split multiple remote networks into multiple phase 2 SAs. Often used with Cisco some products.") + @Param(description = "Split multiple remote Networks into multiple phase 2 SAs. Often used with Cisco some products.") private Boolean splitConnections; @SerializedName(ApiConstants.IKE_VERSION) - @Param(description = "Which IKE Version to use, one of ike (autoselect), ikev1, or ikev2. Defaults to ike") + @Param(description = "Which IKE Version to use, one of ike (autoselect), IKEv1, or IKEv2. Defaults to ike") private String ikeVersion; public void setId(String id) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteVpnGatewayResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteVpnGatewayResponse.java index 1e63ba896c7b..9f997ca5bb7b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteVpnGatewayResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteVpnGatewayResponse.java @@ -31,39 +31,39 @@ @SuppressWarnings("unused") public class Site2SiteVpnGatewayResponse extends BaseResponse implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the vpn gateway ID") + @Param(description = "The VPN gateway ID") private String id; @SerializedName(ApiConstants.PUBLIC_IP) - @Param(description = "the public IP address") + @Param(description = "The public IP address") private String ip; @SerializedName(ApiConstants.VPC_ID) - @Param(description = "the vpc id of this gateway") + @Param(description = "The VPC id of this gateway") private String vpcId; @SerializedName(ApiConstants.VPC_NAME) - @Param(description = "the vpc name of this gateway", since = "4.13.2") + @Param(description = "The VPC name of this gateway", since = "4.13.2") private String vpcName; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the owner") + @Param(description = "The owner") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id") + @Param(description = "The project id") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name") + @Param(description = "The project name") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain id of the owner") + @Param(description = "The domain id of the owner") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name of the owner") + @Param(description = "The domain name of the owner") private String domain; @SerializedName(ApiConstants.DOMAIN_PATH) @@ -71,11 +71,11 @@ public class Site2SiteVpnGatewayResponse extends BaseResponse implements Control private String domainPath; @SerializedName(ApiConstants.REMOVED) - @Param(description = "the date and time the host was removed") + @Param(description = "The date and time the host was removed") private Date removed; @SerializedName(ApiConstants.FOR_DISPLAY) - @Param(description = "is vpn gateway for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) + @Param(description = "Is VPN gateway for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; public void setId(String id) { 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 4ce77cfdf6e2..3a40f3dfa982 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 @@ -30,31 +30,35 @@ @EntityReference(value = SnapshotPolicy.class) public class SnapshotPolicyResponse extends BaseResponseWithTagInformation { @SerializedName("id") - @Param(description = "the ID of the snapshot policy") + @Param(description = "The ID of the Snapshot policy") private String id; @SerializedName("volumeid") - @Param(description = "the ID of the disk volume") + @Param(description = "The ID of the disk volume") private String volumeId; + @SerializedName("volumename") + @Param(description = "the name of the disk volume") + private String volumeName; + @SerializedName("schedule") - @Param(description = "time the snapshot is scheduled to be taken.") + @Param(description = "Time the Snapshot is scheduled to be taken.") private String schedule; @SerializedName("intervaltype") - @Param(description = "the interval type of the snapshot policy") + @Param(description = "The interval type of the Snapshot policy") private short intervalType; @SerializedName("maxsnaps") - @Param(description = "maximum number of snapshots retained") + @Param(description = "Maximum number of Snapshots retained") private int maxSnaps; @SerializedName("timezone") - @Param(description = "the time zone of the snapshot policy") + @Param(description = "The time zone of the Snapshot policy") private String timezone; @SerializedName(ApiConstants.FOR_DISPLAY) - @Param(description = "is this policy for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) + @Param(description = "Is this policy for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; @SerializedName(ApiConstants.ZONE) @@ -87,6 +91,10 @@ public void setVolumeId(String volumeId) { this.volumeId = volumeId; } + public void setVolumeName(String volumeName) { + this.volumeName = volumeName; + } + public String getSchedule() { return schedule; } 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 5d6756c950d6..3db6fd87ed59 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 @@ -32,19 +32,19 @@ @EntityReference(value = Snapshot.class) public class SnapshotResponse extends BaseResponseWithTagInformation implements ControlledViewEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "ID of the snapshot") + @Param(description = "ID of the Snapshot") private String id; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account associated with the snapshot") + @Param(description = "The Account associated with the Snapshot") private String accountName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID of the snapshot's account") + @Param(description = "The domain ID of the Snapshot's Account") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name of the snapshot's account") + @Param(description = "The domain name of the Snapshot's Account") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) @@ -52,15 +52,15 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements private String domainPath; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the snapshot") + @Param(description = "The project id of the Snapshot") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the snapshot") + @Param(description = "The project name of the Snapshot") private String projectName; @SerializedName(ApiConstants.SNAPSHOT_TYPE) - @Param(description = "the type of the snapshot") + @Param(description = "The type of the Snapshot") private String snapshotType; @SerializedName(ApiConstants.VOLUME_ID) @@ -68,11 +68,11 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements private String volumeId; @SerializedName(ApiConstants.VOLUME_NAME) - @Param(description = "name of the disk volume") + @Param(description = "Name of the disk volume") private String volumeName; @SerializedName("volumetype") - @Param(description = "type of the disk volume") + @Param(description = "Type of the disk volume") private String volumeType; @SerializedName(ApiConstants.VOLUME_STATE) @@ -80,23 +80,23 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements private String volumeState; @SerializedName(ApiConstants.CREATED) - @Param(description = " the date the snapshot was created") + @Param(description = "The date the Snapshot was created") private Date created; @SerializedName(ApiConstants.NAME) - @Param(description = "name of the snapshot") + @Param(description = "Name of the Snapshot") private String name; @SerializedName(ApiConstants.INTERVAL_TYPE) - @Param(description = "valid types are hourly, daily, weekly, monthy, template, and none.") + @Param(description = "Valid types are hourly, daily, weekly, monthy, Template, and none.") private String intervalType; @SerializedName(ApiConstants.LOCATION_TYPE) - @Param(description = "valid location types are primary and secondary.") + @Param(description = "Valid location types are primary and secondary.") private String locationType; @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the snapshot. BackedUp means that snapshot is ready to be used; Creating - the snapshot is being allocated on the primary storage; BackingUp - the snapshot is being backed up on secondary storage") + @Param(description = "The state of the Snapshot. BackedUp means that Snapshot is ready to be used; Creating - the Snapshot is being allocated on the primary storage; BackingUp - the Snapshot is being backed up on secondary storage") private Snapshot.State state; @SerializedName(ApiConstants.STATUS) @@ -104,7 +104,7 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements private String status; @SerializedName(ApiConstants.PHYSICAL_SIZE) - @Param(description = "physical size of backedup snapshot on image store") + @Param(description = "Physical size of backed up Snapshot on image store") private long physicalSize; @SerializedName(ApiConstants.CHAIN_SIZE) @@ -112,7 +112,7 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements private Long chainSize; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "id of the availability zone") + @Param(description = "ID of the availability zone") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) @@ -120,19 +120,19 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements private String zoneName; @SerializedName(ApiConstants.REVERTABLE) - @Param(description = "indicates whether the underlying storage supports reverting the volume to this snapshot") + @Param(description = "Indicates whether the underlying storage supports reverting the volume to this Snapshot") private boolean revertable; @SerializedName(ApiConstants.OS_TYPE_ID) - @Param(description = "id of the os on volume", since = "4.10") + @Param(description = "ID of the os on volume", since = "4.10") private String osTypeId; @SerializedName(ApiConstants.OS_DISPLAY_NAME) - @Param(description = "display name of the os on volume") + @Param(description = "Display name of the os on volume") private String osDisplayName; @SerializedName(ApiConstants.VIRTUAL_SIZE) - @Param(description = "virtual size of backedup snapshot on image store") + @Param(description = "Virtual size of backedup Snapshot on image store") private long virtualSize; @SerializedName(ApiConstants.DATASTORE_ID) @@ -155,6 +155,14 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements @Param(description = "download progress of a snapshot", since = "4.19.0") private Map downloadDetails; + @SerializedName("parent") + @Param(description = "The parent ID of the Snapshot", since = "4.22.1") + private String parent; + + @SerializedName("parentname") + @Param(description = "The parent name of the Snapshot", since = "4.22.1") + private String parentName; + public SnapshotResponse() { tags = new LinkedHashSet(); } @@ -313,4 +321,12 @@ public void setDatastoreType(String datastoreType) { public void setDownloadDetails(Map downloadDetails) { this.downloadDetails = downloadDetails; } + + public void setParent(String parent) { + this.parent = parent; + } + + public void setParentName(String parentName) { + this.parentName = parentName; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/SnapshotScheduleResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SnapshotScheduleResponse.java index 7bc8ee8cba26..d8ce673d335f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/SnapshotScheduleResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/SnapshotScheduleResponse.java @@ -26,19 +26,19 @@ public class SnapshotScheduleResponse extends BaseResponse { @SerializedName("id") - @Param(description = "the ID of the snapshot schedule") + @Param(description = "The ID of the Snapshot schedule") private String id; @SerializedName("volumeid") - @Param(description = "the volume ID the snapshot schedule applied for") + @Param(description = "The volume ID the Snapshot schedule applied for") private String volumeId; @SerializedName("snapshotpolicyid") - @Param(description = "the snapshot policy ID used by the snapshot schedule") + @Param(description = "The Snapshot policy ID used by the Snapshot schedule") private String snapshotPolicyId; @SerializedName("scheduled") - @Param(description = "time the snapshot is scheduled to be taken") + @Param(description = "Time the Snapshot is scheduled to be taken") private Date scheduled; public String getId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/SslCertResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SslCertResponse.java index aa729f123b44..d4add82f666c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/SslCertResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/SslCertResponse.java @@ -27,8 +27,6 @@ import org.apache.cloudstack.network.tls.SslCert; import com.cloud.serializer.Param; -//import org.apache.cloudstack.api.EntityReference; - @EntityReference(value = SslCert.class) public class SslCertResponse extends BaseResponse { @@ -37,35 +35,35 @@ public class SslCertResponse extends BaseResponse { private String id; @SerializedName(ApiConstants.CERTIFICATE) - @Param(description = "certificate") + @Param(description = "Certificate") private String certificate; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "account for the certificate") + @Param(description = "Account for the certificate") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the certificate") + @Param(description = "The project id of the certificate") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the certificate") + @Param(description = "The project name of the certificate") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain id of the network owner") + @Param(description = "The domain id of the Network owner") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name of the network owner") + @Param(description = "The domain name of the Network owner") private String domain; @SerializedName(ApiConstants.CERTIFICATE_CHAIN) - @Param(description = "certificate chain") + @Param(description = "Certificate chain") private String certchain; @SerializedName(ApiConstants.CERTIFICATE_FINGERPRINT) - @Param(description = "certificate fingerprint") + @Param(description = "Certificate fingerprint") private String fingerprint; @SerializedName(ApiConstants.LOAD_BALANCER_RULE_LIST) @@ -73,7 +71,7 @@ public class SslCertResponse extends BaseResponse { List lbIds; @SerializedName(ApiConstants.NAME) - @Param(description = "name") + @Param(description = "Name") private String name; public SslCertResponse() { 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 9008fce2392b..ed0e050b9652 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 @@ -31,11 +31,11 @@ @SuppressWarnings("unused") public class StaticRouteResponse extends BaseResponse implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of static route") + @Param(description = "The ID of static route") private String id; @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the static route") + @Param(description = "The state of the static route") private String state; @SerializedName(ApiConstants.VPC_ID) @@ -55,27 +55,27 @@ public class StaticRouteResponse extends BaseResponse implements ControlledEntit private String nextHop; @SerializedName(ApiConstants.CIDR) - @Param(description = "static route CIDR") + @Param(description = "Static route CIDR") private String cidr; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account associated with the static route") + @Param(description = "The Account associated with the static route") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the static route") + @Param(description = "The project ID of the static route") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the static route") + @Param(description = "The project name of the static route") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the ID of the domain associated with the static route") + @Param(description = "The ID of the domain associated with the static route") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain associated with the static route") + @Param(description = "The domain associated with the static route") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) @@ -83,7 +83,7 @@ public class StaticRouteResponse extends BaseResponse implements ControlledEntit private String domainPath; @SerializedName(ApiConstants.TAGS) - @Param(description = "the list of resource tags associated with static route", responseObject = ResourceTagResponse.class) + @Param(description = "The list of resource tags associated with static route", responseObject = ResourceTagResponse.class) private List tags; @Override 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 287d78bb612f..232daa4f4a9a 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,59 +27,59 @@ 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 Instance stats were collected. The format is 'yyyy-MM-dd hh:mm:ss'") private Date timestamp; @SerializedName("cpuused") - @Param(description = "the amount (percentage) of the VM's CPU currently used") + @Param(description = "The amount (percentage) of the Instance's CPU currently used") private String cpuUsed; @SerializedName(ApiConstants.DISK_IO_READ) - @Param(description = "the VM's disk number of read requests (IO) made in the last collection cycle as defined by vm.stats.interval configuration") + @Param(description = "The Instance's disk number of read requests (IO) made in the last collection cycle as defined by vm.stats.interval configuration") protected Long diskIORead; @SerializedName(ApiConstants.DISK_IO_WRITE) - @Param(description = "the VM's disk number of write requests (IO) made in the last collection cycle as defined by vm.stats.interval configuration") + @Param(description = "The Instance's disk number of write requests (IO) made in the last collection cycle as defined by vm.stats.interval configuration") protected Long diskIOWrite; @SerializedName(ApiConstants.DISK_IO_PSTOTAL) - @Param(description = "the total disk iops since the last stats retrieval") + @Param(description = "The total disk IOPS since the last stats retrieval") protected Long diskIopsTotal = 0L; @SerializedName(ApiConstants.DISK_KBS_READ) - @Param(description = "the VM's disk read in KiB") + @Param(description = "The Instance's disk read in KiB") private Long diskKbsRead; @SerializedName(ApiConstants.DISK_KBS_WRITE) - @Param(description = "the VM's disk write in KiB") + @Param(description = "The Instance's disk write in KiB") private Long diskKbsWrite; @SerializedName("memoryintfreekbs") - @Param(description = "the VM's free memory in KB or -1 if it cannot be gathered") + @Param(description = "The Instance's free memory in KB or -1 if it cannot be gathered") private Long memoryIntFreeKBs; @SerializedName("memorykbs") - @Param(description = "the memory used by the VM in KB") + @Param(description = "The memory used by the Instance in KB") private Long memoryKBs; @SerializedName("memorytargetkbs") - @Param(description = "the target memory in VM (KB)") + @Param(description = "The target memory in Instance (KB)") private Long memoryTargetKBs; @SerializedName("networkkbsread") - @Param(description = "the incoming network traffic on the VM in KiB") + @Param(description = "The incoming Network traffic on the Instance in KiB") protected Long networkKbsRead; @SerializedName("networkkbswrite") - @Param(description = "the outgoing network traffic on the host in KiB") + @Param(description = "The outgoing Network traffic on the host in KiB") protected Long networkKbsWrite; @SerializedName("networkread") - @Param(description = "the amount of downloaded data by the VM in MiB") + @Param(description = "The amount of downloaded data by the Instance in MiB") protected String networkRead; @SerializedName("networkwrite") - @Param(description = "the amount of uploaded data by the VM in MiB") + @Param(description = "The amount of uploaded data by the Instance in MiB") protected String networkWrite; public void setTimestamp(Date timestamp) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/StorageNetworkIpRangeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/StorageNetworkIpRangeResponse.java index 1dc60ce67f39..963d4d18ba60 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/StorageNetworkIpRangeResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/StorageNetworkIpRangeResponse.java @@ -28,39 +28,39 @@ @EntityReference(value = StorageNetworkIpRange.class) public class StorageNetworkIpRangeResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the uuid of storage network IP range.") + @Param(description = "The UUID of storage Network IP range.") private String uuid; @SerializedName(ApiConstants.VLAN) - @Param(description = "the ID or VID of the VLAN.") + @Param(description = "The ID or VID of the VLAN.") private Integer vlan; @SerializedName(ApiConstants.POD_ID) - @Param(description = "the Pod uuid for the storage network IP range") + @Param(description = "The Pod UUID for the storage Network IP range") private String podUuid; @SerializedName(ApiConstants.START_IP) - @Param(description = "the start ip of the storage network IP range") + @Param(description = "The start IP of the storage Network IP range") private String startIp; @SerializedName(ApiConstants.END_IP) - @Param(description = "the end ip of the storage network IP range") + @Param(description = "The end IP of the storage Network IP range") private String endIp; @SerializedName(ApiConstants.GATEWAY) - @Param(description = "the gateway of the storage network IP range") + @Param(description = "The gateway of the storage Network IP range") private String gateway; @SerializedName(ApiConstants.NETWORK_ID) - @Param(description = "the network uuid of storage network IP range") + @Param(description = "The Network UUID of storage Network IP range") private String networkUuid; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the Zone uuid of the storage network IP range") + @Param(description = "The Zone UUID of the storage Network IP range") private String zoneUuid; @SerializedName(ApiConstants.NETMASK) - @Param(description = "the netmask of the storage network IP range") + @Param(description = "The netmask of the storage Network IP range") private String netmask; public void setUuid(String uuid) { 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 abc674ff0f9a..25e67115ab4a 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 @@ -30,71 +30,76 @@ @EntityReference(value = StoragePool.class) public class StoragePoolResponse extends BaseResponseWithAnnotations { @SerializedName("id") - @Param(description = "the ID of the storage pool") + @Param(description = "The ID of the storage pool") private String id; @SerializedName("zoneid") - @Param(description = "the Zone ID of the storage pool") + @Param(description = "The Zone ID of the storage pool") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "the Zone name of the storage pool") + @Param(description = "The Zone name of the storage pool") private String zoneName; @SerializedName("podid") - @Param(description = "the Pod ID of the storage pool") + @Param(description = "The Pod ID of the storage pool") private String podId; @SerializedName("podname") - @Param(description = "the Pod name of the storage pool") + @Param(description = "The Pod name of the storage pool") private String podName; @SerializedName("name") - @Param(description = "the name of the storage pool") + @Param(description = "The name of the storage pool") private String name; @SerializedName("ipaddress") - @Param(description = "the IP address of the storage pool") + @Param(description = "The IP address of the storage pool") private String ipAddress; @SerializedName("path") - @Param(description = "the storage pool path") + @Param(description = "The storage pool path") private String path; @SerializedName("created") - @Param(description = "the date and time the storage pool was created") + @Param(description = "The date and time the storage pool was created") private Date created; @SerializedName("type") - @Param(description = "the storage pool type") + @Param(description = "The storage pool type") private String type; @SerializedName("clusterid") - @Param(description = "the ID of the cluster for the storage pool") + @Param(description = "The ID of the cluster for the storage pool") private String clusterId; @SerializedName("clustername") - @Param(description = "the name of the cluster for the storage pool") + @Param(description = "The name of the cluster for the storage pool") private String clusterName; + @SerializedName(ApiConstants.CAPACITY_BYTES) + @Param(description = "bytes CloudStack can provision from this storage pool", since = "4.22.0") + private Long capacityBytes; + + @Deprecated(since = "4.22.0") @SerializedName("disksizetotal") - @Param(description = "the total disk size of the storage pool") + @Param(description = "The total disk size of the storage pool") private Long diskSizeTotal; @SerializedName("disksizeallocated") - @Param(description = "the host's currently allocated disk size") + @Param(description = "The pool's currently allocated disk size") private Long diskSizeAllocated; @SerializedName("disksizeused") - @Param(description = "the host's currently used disk size") + @Param(description = "The pool's currently used disk size") private Long diskSizeUsed; - @SerializedName("capacityiops") + @SerializedName(ApiConstants.CAPACITY_IOPS) @Param(description = "IOPS CloudStack can provision from this storage pool") private Long capacityIops; @SerializedName("allocatediops") - @Param(description = "total min IOPS currently in use by volumes") + @Param(description = "Total min IOPS currently in use by volumes") private Long allocatedIops; @SerializedName(ApiConstants.USED_IOPS) @@ -106,7 +111,7 @@ public class StoragePoolResponse extends BaseResponseWithAnnotations { private Map customStats; @SerializedName("tags") - @Param(description = "the tags for the storage pool") + @Param(description = "The tags for the storage pool") private String tags; @SerializedName(ApiConstants.STORAGE_ACCESS_GROUPS) @@ -122,23 +127,23 @@ public class StoragePoolResponse extends BaseResponseWithAnnotations { private Boolean isTagARule; @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the storage pool") + @Param(description = "The state of the storage pool") private StoragePoolStatus state; @SerializedName(ApiConstants.SCOPE) - @Param(description = "the scope of the storage pool") + @Param(description = "The scope of the storage pool") private String scope; @SerializedName("overprovisionfactor") - @Param(description = "the overprovisionfactor for the storage pool", since = "4.4") + @Param(description = "The overprovisionfactor for the storage pool", since = "4.4") private String overProvisionFactor; @SerializedName(ApiConstants.HYPERVISOR) - @Param(description = "the hypervisor type of the storage pool") + @Param(description = "The hypervisor type of the storage pool") private String hypervisor; @SerializedName("suitableformigration") - @Param(description = "true if this pool is suitable to migrate a volume," + " false otherwise") + @Param(description = "True if this pool is suitable to migrate a volume," + " false otherwise") private Boolean suitableForMigration; @SerializedName("provider") @@ -146,7 +151,7 @@ public class StoragePoolResponse extends BaseResponseWithAnnotations { private String provider; @SerializedName(ApiConstants.STORAGE_CAPABILITIES) - @Param(description = "the storage pool capabilities") + @Param(description = "The storage pool capabilities") private Map caps; @SerializedName(ApiConstants.MANAGED) @@ -288,6 +293,14 @@ public void setClusterName(String clusterName) { this.clusterName = clusterName; } + public Long getCapacityBytes() { + return capacityBytes; + } + + public void setCapacityBytes(Long capacityBytes) { + this.capacityBytes = capacityBytes; + } + public Long getDiskSizeTotal() { return diskSizeTotal; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/StorageProviderResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/StorageProviderResponse.java index d68979a5bcde..e063b88335a5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/StorageProviderResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/StorageProviderResponse.java @@ -26,11 +26,11 @@ public class StorageProviderResponse extends BaseResponse { @SerializedName("name") - @Param(description = "the name of the storage provider") + @Param(description = "The name of the storage provider") private String name; @SerializedName("type") - @Param(description = "the type of the storage provider: primary or image provider") + @Param(description = "The type of the storage provider: primary or image provider") private String type; /** diff --git a/api/src/main/java/org/apache/cloudstack/api/response/StorageTagResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/StorageTagResponse.java index 7d8db39b4e92..fc6527f7e4c8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/StorageTagResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/StorageTagResponse.java @@ -22,15 +22,15 @@ public class StorageTagResponse extends BaseResponse { @SerializedName("id") - @Param(description = "the ID of the storage tag") + @Param(description = "The ID of the storage tag") private String id; @SerializedName("poolid") - @Param(description = "the pool ID of the storage tag") + @Param(description = "The pool ID of the storage tag") private long poolId; @SerializedName("name") - @Param(description = "the name of the storage tag") + @Param(description = "The name of the storage tag") private String name; public String getId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/SuccessResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SuccessResponse.java index 0dde6d0e4230..3c03fb7b3bc4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/SuccessResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/SuccessResponse.java @@ -24,11 +24,11 @@ public class SuccessResponse extends BaseResponse { @SerializedName("success") - @Param(description = "true if operation is executed successfully") + @Param(description = "True if operation is executed successfully") private Boolean success = true; @SerializedName("displaytext") - @Param(description = "any text associated with the success or failure") + @Param(description = "Any text associated with the success or failure") private String displayText; public Boolean getSuccess() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/SystemVmInstanceResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SystemVmInstanceResponse.java index 5e0638080b9e..5f94537d4dbd 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/SystemVmInstanceResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/SystemVmInstanceResponse.java @@ -27,27 +27,27 @@ */ public class SystemVmInstanceResponse extends BaseResponse { @SerializedName("id") - @Param(description = "the ID of the system VM") + @Param(description = "The ID of the System VM") private String id; @SerializedName("systemvmtype") - @Param(description = "the system VM type") + @Param(description = "The System VM type") private String systemVmType; @SerializedName("name") - @Param(description = "the name of the system VM") + @Param(description = "The name of the System VM") private String name; @SerializedName("hostid") - @Param(description = "the host ID for the system VM") + @Param(description = "The host ID for the System VM") private String hostId; @SerializedName("state") - @Param(description = "the state of the system VM") + @Param(description = "The state of the System VM") private String state; @SerializedName("role") - @Param(description = "the role of the system VM") + @Param(description = "The role of the System VM") private String role; public String getId() { 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 7b1784fc7673..a3ed88c27356 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 @@ -31,143 +31,147 @@ @EntityReference(value = VirtualMachine.class) public class SystemVmResponse extends BaseResponseWithAnnotations { @SerializedName("id") - @Param(description = "the ID of the system VM") + @Param(description = "The ID of the System VM") private String id; @SerializedName("systemvmtype") - @Param(description = "the system VM type") + @Param(description = "The System VM type") private String systemVmType; @SerializedName("zoneid") - @Param(description = "the Zone ID for the system VM") + @Param(description = "The Zone ID for the System VM") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "the Zone name for the system VM") + @Param(description = "The Zone name for the System VM") private String zoneName; @SerializedName("dns1") - @Param(description = "the first DNS for the system VM") + @Param(description = "The first DNS for the System VM") private String dns1; @SerializedName("dns2") - @Param(description = "the second DNS for the system VM") + @Param(description = "The second DNS for the System VM") private String dns2; @SerializedName("networkdomain") - @Param(description = "the network domain for the system VM") + @Param(description = "The Network domain for the System VM") private String networkDomain; @SerializedName("gateway") - @Param(description = "the gateway for the system VM") + @Param(description = "The gateway for the System VM") private String gateway; @SerializedName("name") - @Param(description = "the name of the system VM") + @Param(description = "The name of the System VM") private String name; @SerializedName("podid") - @Param(description = "the Pod ID for the system VM") + @Param(description = "The Pod ID for the System VM") private String podId; @SerializedName("podname") - @Param(description = "the Pod name for the system VM", since = "4.13.2") + @Param(description = "The Pod name for the System VM", since = "4.13.2") private String podName; @SerializedName("hostid") - @Param(description = "the host ID for the system VM") + @Param(description = "The host ID for the System VM") private String hostId; @SerializedName("hostname") - @Param(description = "the hostname for the system VM") + @Param(description = "The hostname for the System VM") private String hostName; @SerializedName(ApiConstants.HOST_CONTROL_STATE) - @Param(description = "the control state of the host for the system VM") + @Param(description = "The control state of the host for the System VM") private String hostControlState; @SerializedName("hypervisor") - @Param(description = "the hypervisor on which the template runs") + @Param(description = "The hypervisor on which the Template runs") private String hypervisor; @SerializedName(ApiConstants.PRIVATE_IP) - @Param(description = "the private IP address for the system VM") + @Param(description = "The private IP address for the System VM") private String privateIp; @SerializedName(ApiConstants.PRIVATE_MAC_ADDRESS) - @Param(description = "the private MAC address for the system VM") + @Param(description = "The private MAC address for the System VM") private String privateMacAddress; @SerializedName(ApiConstants.PRIVATE_NETMASK) - @Param(description = "the private netmask for the system VM") + @Param(description = "The private netmask for the System VM") private String privateNetmask; @SerializedName(ApiConstants.LINK_LOCAL_IP) - @Param(description = "the link local IP address for the system vm") + @Param(description = "The Control IP address for the System VM") private String linkLocalIp; @SerializedName(ApiConstants.LINK_LOCAL_MAC_ADDRESS) - @Param(description = "the link local MAC address for the system vm") + @Param(description = "The link local MAC address for the System VM") private String linkLocalMacAddress; @SerializedName(ApiConstants.LINK_LOCAL_MAC_NETMASK) - @Param(description = "the link local netmask for the system vm") + @Param(description = "The link local netmask for the System VM") private String linkLocalNetmask; @SerializedName("publicip") - @Param(description = "the public IP address for the system VM") + @Param(description = "The public IP address for the System VM") private String publicIp; @SerializedName("publicmacaddress") - @Param(description = "the public MAC address for the system VM") + @Param(description = "The public MAC address for the System VM") private String publicMacAddress; @SerializedName("publicnetmask") - @Param(description = "the public netmask for the system VM") + @Param(description = "The public netmask for the System VM") private String publicNetmask; + @SerializedName("storageip") + @Param(description = "the ip address for the system VM on the storage network") + private String storageIp; + @SerializedName("templateid") - @Param(description = "the template ID for the system VM") + @Param(description = "The Template ID for the System VM") private String templateId; @SerializedName("templatename") - @Param(description = "the template name for the system VM", since = "4.13.2") + @Param(description = "The Template name for the System VM", since = "4.13.2") private String templateName; @SerializedName("created") - @Param(description = "the date and time the system VM was created") + @Param(description = "The date and time the System VM was created") private Date created; @SerializedName("state") - @Param(description = "the state of the system VM") + @Param(description = "The state of the System VM") private String state; @SerializedName("agentstate") - @Param(description = "the agent state of the system VM", since = "4.13.1") + @Param(description = "The agent state of the System VM", since = "4.13.1") private String agentState; @SerializedName("activeviewersessions") - @Param(description = "the number of active console sessions for the console proxy system vm") + @Param(description = "The number of active console sessions for the console proxy System VM") private Integer activeViewerSessions; @SerializedName("guestvlan") - @Param(description = "guest vlan range") + @Param(description = "Guest VLAN range") private String guestVlan; @SerializedName("publicvlan") - @Param(description = "public vlan range") + @Param(description = "Public VLAN range") private List publicVlan; @SerializedName("disconnected") - @Param(description = "the last disconnected date of host", since = "4.13.1") + @Param(description = "The last disconnected date of host", since = "4.13.1") private Date disconnectedOn; @SerializedName("version") - @Param(description = "the systemvm agent version", since = "4.13.1") + @Param(description = "The systemvm agent version", since = "4.13.1") private String version; @SerializedName(ApiConstants.IS_DYNAMICALLY_SCALABLE) - @Param(description = "true if vm contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory.") + @Param(description = "True if the Instance contains XS/VMWare tools in order to support dynamic scaling of Instance CPU/memory.") private Boolean isDynamicallyScalable; @SerializedName(ApiConstants.SERVICE_OFFERING_ID) @@ -355,6 +359,14 @@ public void setPublicNetmask(String publicNetmask) { this.publicNetmask = publicNetmask; } + public String getStorageIp() { + return storageIp; + } + + public void setStorageIp(String storageIp) { + this.storageIp = storageIp; + } + public String getTemplateId() { return templateId; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/TemplateOVFPropertyResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/TemplateOVFPropertyResponse.java index ebe0d1cfd0db..715d0e980ef4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/TemplateOVFPropertyResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/TemplateOVFPropertyResponse.java @@ -27,43 +27,43 @@ public class TemplateOVFPropertyResponse extends BaseResponse { @SerializedName(ApiConstants.KEY) - @Param(description = "the ovf property key") + @Param(description = "The ovf property key") private String key; @SerializedName(ApiConstants.TYPE) - @Param(description = "the ovf property type") + @Param(description = "The ovf property type") private String type; @SerializedName(ApiConstants.VALUE) - @Param(description = "the ovf property value") + @Param(description = "The ovf property value") private String value; @SerializedName(ApiConstants.PASSWORD) - @Param(description = "is the ovf property a password") + @Param(description = "Is the ovf property a password") private Boolean password; @SerializedName(ApiConstants.QUALIFIERS) - @Param(description = "the ovf property qualifiers") + @Param(description = "The ovf property qualifiers") private String qualifiers; @SerializedName(ApiConstants.USER_CONFIGURABLE) - @Param(description = "is the ovf property user configurable") + @Param(description = "Is the ovf property user configurable") private Boolean userConfigurable; @SerializedName(ApiConstants.LABEL) - @Param(description = "the ovf property label") + @Param(description = "The ovf property label") private String label; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "the ovf property label") + @Param(description = "The ovf property label") private String description; @SerializedName(ApiConstants.INDEX) - @Param(description = "the ovf property index") + @Param(description = "The ovf property index") private Integer index; @SerializedName(ApiConstants.CATEGORY) - @Param(description = "the ovf property category") + @Param(description = "The ovf property category") private String category; @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/response/TemplatePermissionsResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/TemplatePermissionsResponse.java index 5f0b9a58c7c8..c489deaffe5b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/TemplatePermissionsResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/TemplatePermissionsResponse.java @@ -31,23 +31,23 @@ @SuppressWarnings("unused") public class TemplatePermissionsResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the template ID") + @Param(description = "The Template ID") private String id; @SerializedName(ApiConstants.IS_PUBLIC) - @Param(description = "true if this template is a public template, false otherwise") + @Param(description = "True if this Template is a public Template, false otherwise") private Boolean publicTemplate; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the ID of the domain to which the template belongs") + @Param(description = "The ID of the domain to which the Template belongs") private String domainId; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the list of accounts the template is available for") + @Param(description = "The list of Accounts the Template is available for") private List accountNames; @SerializedName(ApiConstants.PROJECT_IDS) - @Param(description = "the list of projects the template is available for") + @Param(description = "The list of projects the Template is available for") private List projectIds; public void setId(String id) { 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 a94dbd95a56f..2e706586f01d 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 @@ -36,153 +36,153 @@ @SuppressWarnings("unused") public class TemplateResponse extends BaseResponseWithTagInformation implements ControlledViewEntityResponse, SetResourceIconResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the template ID") + @Param(description = "The Template ID") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the template name") + @Param(description = "The Template name") private String name; @SerializedName(ApiConstants.DISPLAY_TEXT) - @Param(description = "the template display text") + @Param(description = "The Template display text") private String displayText; @SerializedName(ApiConstants.IS_PUBLIC) // propName="public" (FIXME: this used to be part of Param annotation, do we need it?) - @Param(description = "true if this template is a public template, false otherwise") + @Param(description = "True if this Template is a public Template, false otherwise") private boolean isPublic; @SerializedName(ApiConstants.CREATED) - @Param(description = "the date this template was created") + @Param(description = "The date this Template was created") private Date created; @SerializedName(ApiConstants.REMOVED) - @Param(description = "the date this template was removed") + @Param(description = "The date this Template was removed") private Date removed; @SerializedName(ApiConstants.IS_READY) // propName="ready" (FIXME: this used to be part of Param annotation, do we need it?) - @Param(description = "true if the template is ready to be deployed from, false otherwise.") + @Param(description = "True if the Template is ready to be deployed from, false otherwise.") private boolean isReady; @SerializedName(ApiConstants.PASSWORD_ENABLED) - @Param(description = "true if the reset password feature is enabled, false otherwise") + @Param(description = "True if the reset password feature is enabled, false otherwise") private Boolean passwordEnabled; @SerializedName(ApiConstants.FORMAT) - @Param(description = "the format of the template.") + @Param(description = "The format of the Template.") private ImageFormat format; @SerializedName(ApiConstants.BOOTABLE) - @Param(description = "true if the ISO is bootable, false otherwise") + @Param(description = "True if the ISO is bootable, false otherwise") private Boolean bootable; @SerializedName(ApiConstants.IS_FEATURED) - @Param(description = "true if this template is a featured template, false otherwise") + @Param(description = "True if this Template is a featured Template, false otherwise") private boolean featured; @SerializedName(ApiConstants.CROSS_ZONES) - @Param(description = "true if the template is managed across all Zones, false otherwise") + @Param(description = "True if the Template is managed across all Zones, false otherwise") private boolean crossZones; @SerializedName(ApiConstants.OS_TYPE_ID) - @Param(description = "the ID of the OS type for this template.") + @Param(description = "The ID of the OS type for this Template.") private String osTypeId; @SerializedName("ostypename") - @Param(description = "the name of the OS type for this template.") + @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") + @Param(description = "The Account id to which the Template belongs") private String accountId; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account name to which the template belongs") + @Param(description = "The Account name to which the Template belongs") private String account; //TODO: since a template can be associated to more than one zones, this model is not accurate. For backward-compatibility, keep these fields // here, but add a zones field to capture multiple zones. @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the ID of the zone for this template") + @Param(description = "The ID of the zone for this Template") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "the name of the zone for this template") + @Param(description = "The name of the zone for this Template") private String zoneName; @SerializedName(ApiConstants.STATUS) - @Param(description = "the status of the template") + @Param(description = "The status of the Template") private String status; @SerializedName(ApiConstants.SIZE) - @Param(description = "the size of the template") + @Param(description = "The size of the Template") private Long size; @SerializedName(ApiConstants.PHYSICAL_SIZE) - @Param(description = "the physical size of the template") + @Param(description = "The physical size of the Template") private Long physicalSize; @SerializedName(ApiConstants.TEMPLATE_TYPE) - @Param(description = "the type of the template") + @Param(description = "The type of the Template") private String templateType; @SerializedName(ApiConstants.HYPERVISOR) - @Param(description = "the hypervisor on which the template runs") + @Param(description = "The hypervisor on which the Template runs") private String hypervisor; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the name of the domain to which the template belongs") + @Param(description = "The name of the domain to which the Template belongs") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) - @Param(description = "path of the Domain the template belongs to", since = "4.19.2.0") + @Param(description = "Path of the Domain the template belongs to", since = "4.19.2.0") private String domainPath; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the ID of the domain to which the template belongs") + @Param(description = "The ID of the domain to which the Template belongs") private String domainId; @SerializedName(ApiConstants.IS_EXTRACTABLE) - @Param(description = "true if the template is extractable, false otherwise") + @Param(description = "True if the Template is extractable, false otherwise") private Boolean extractable; @SerializedName(ApiConstants.CHECKSUM) - @Param(description = "checksum of the template") + @Param(description = "Checksum of the Template") private String checksum; @SerializedName(ApiConstants.SOURCETEMPLATEID) - @Param(description = "the template ID of the parent template if present") + @Param(description = "The Template ID of the parent Template if present") private String sourcetemplateId; @SerializedName(ApiConstants.HOST_ID) - @Param(description = "the ID of the secondary storage host for the template") + @Param(description = "The ID of the secondary storage host for the Template") private String hostId; @SerializedName(ApiConstants.HOST_NAME) - @Param(description = "the name of the secondary storage host for the template") + @Param(description = "The name of the secondary storage host for the Template") private String hostName; @SerializedName(ApiConstants.TEMPLATE_TAG) - @Param(description = "the tag of this template") + @Param(description = "The tag of this Template") private String templateTag; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the template") + @Param(description = "The project ID of the Template") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the template") + @Param(description = "The project name of the Template") private String projectName; @SerializedName(ApiConstants.DETAILS) - @Param(description = "additional key/value details tied with template") + @Param(description = "Additional key/value details tied with Template") private Map details; @SerializedName(ApiConstants.DOWNLOAD_DETAILS) - @Param(description = "Lists the download progress of a template across all secondary storages") + @Param(description = "Lists the download progress of a Template across all secondary storages") private List> downloadDetails; @SerializedName(ApiConstants.ARCH) @@ -190,23 +190,23 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements private String arch; @SerializedName(ApiConstants.BITS) - @Param(description = "the processor bit size", since = "4.10") + @Param(description = "The processor bit size", since = "4.10") private int bits; @SerializedName(ApiConstants.SSHKEY_ENABLED) - @Param(description = "true if template is sshkey enabled, false otherwise") + @Param(description = "True if Template is sshkey enabled, false otherwise") private Boolean sshKeyEnabled; @SerializedName(ApiConstants.IS_DYNAMICALLY_SCALABLE) - @Param(description = "true if template contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory") + @Param(description = "True if Template contains XS/VMWare tools in order to support dynamic scaling of Instance CPU/memory") private Boolean isDynamicallyScalable; @SerializedName(ApiConstants.DIRECT_DOWNLOAD) - @Param(description = "KVM Only: true if template is directly downloaded to Primary Storage bypassing Secondary Storage") + @Param(description = "KVM Only: true if Template is directly downloaded to Primary Storage bypassing Secondary Storage") private Boolean directDownload; @SerializedName(ApiConstants.DEPLOY_AS_IS) - @Param(description = "VMware only: true if template is deployed without orchestrating disks and networks but \"as-is\" defined in the template.", + @Param(description = "VMware only: true if Template is deployed without orchestrating disks and Networks but \"as-is\" defined in the Template.", since = "4.15") private Boolean deployAsIs; @@ -216,42 +216,42 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements private Boolean forCks; @SerializedName(ApiConstants.DEPLOY_AS_IS_DETAILS) - @Param(description = "VMware only: additional key/value details tied with deploy-as-is template", + @Param(description = "VMware only: additional key/value details tied with deploy-as-is Template", since = "4.15") private Map deployAsIsDetails; @SerializedName("parenttemplateid") - @Param(description = "if Datadisk template, then id of the root disk template this template belongs to") + @Param(description = "If Datadisk Template, then id of the root disk Template this Template belongs to") @Deprecated(since = "4.15") private String parentTemplateId; @SerializedName("childtemplates") - @Param(description = "if root disk template, then ids of the datas disk templates this template owns") + @Param(description = "If root disk Template, then IDs of the datas disk Templates this Template owns") @Deprecated(since = "4.15") private Set childTemplates; @SerializedName(ApiConstants.REQUIRES_HVM) - @Param(description = "true if template requires HVM enabled, false otherwise") + @Param(description = "True if Template requires HVM enabled, false otherwise") private Boolean requiresHvm; @SerializedName(ApiConstants.URL) - @Param(description = "the URL which the template/iso is registered from") + @Param(description = "The URL which the Template/ISO is registered from") private String url; @SerializedName(ApiConstants.RESOURCE_ICON) @Param(description = "Base64 string representation of the resource icon", since = "4.16.0.0") ResourceIconResponse icon; - @SerializedName(ApiConstants.USER_DATA_ID) @Param(description="the id of userdata linked to this template", since = "4.18.0") + @SerializedName(ApiConstants.USER_DATA_ID) @Param(description = "The id of userdata linked to this Template", since = "4.18.0") private String userDataId; - @SerializedName(ApiConstants.USER_DATA_NAME) @Param(description="the name of userdata linked to this template", since = "4.18.0") + @SerializedName(ApiConstants.USER_DATA_NAME) @Param(description = "The name of userdata linked to this Template", since = "4.18.0") private String userDataName; - @SerializedName(ApiConstants.USER_DATA_POLICY) @Param(description="the userdata override policy with the userdata provided while deploying VM", since = "4.18.0") + @SerializedName(ApiConstants.USER_DATA_POLICY) @Param(description = "The userdata override policy with the userdata provided while deploying Instance", since = "4.18.0") private String userDataPolicy; - @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") + @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") diff --git a/api/src/main/java/org/apache/cloudstack/api/response/TrafficMonitorResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/TrafficMonitorResponse.java index b19c422a5af0..0251b2ab4550 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/TrafficMonitorResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/TrafficMonitorResponse.java @@ -26,23 +26,23 @@ public class TrafficMonitorResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the external firewall") + @Param(description = "The ID of the external firewall") private String id; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the zone ID of the external firewall") + @Param(description = "The zone ID of the external firewall") private String zoneId; @SerializedName(ApiConstants.IP_ADDRESS) - @Param(description = "the management IP address of the external firewall") + @Param(description = "The management IP address of the external firewall") private String ipAddress; @SerializedName(ApiConstants.NUM_RETRIES) - @Param(description = "the number of times to retry requests to the external firewall") + @Param(description = "The number of times to retry requests to the external firewall") private String numRetries; @SerializedName(ApiConstants.TIMEOUT) - @Param(description = "the timeout (in seconds) for requests to the external firewall") + @Param(description = "The timeout (in seconds) for requests to the external firewall") private String timeout; public String getId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/TrafficTypeImplementorResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/TrafficTypeImplementorResponse.java index ebcd7c9a0d4b..29512c2cdd5d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/TrafficTypeImplementorResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/TrafficTypeImplementorResponse.java @@ -25,11 +25,11 @@ public class TrafficTypeImplementorResponse extends BaseResponse { @SerializedName(ApiConstants.TRAFFIC_TYPE) - @Param(description = "network traffic type") + @Param(description = "Network traffic type") private String trafficType; @SerializedName(ApiConstants.TRAFFIC_TYPE_IMPLEMENTOR) - @Param(description = "implementor of network traffic type") + @Param(description = "Implementor of Network traffic type") private String implementor; public void setTrafficType(String type) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/TrafficTypeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/TrafficTypeResponse.java index 9a79b0724658..2b8af97f160e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/TrafficTypeResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/TrafficTypeResponse.java @@ -29,35 +29,43 @@ public class TrafficTypeResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "id of the network provider") + @Param(description = "ID of the Network provider") private String id; @SerializedName(ApiConstants.TRAFFIC_TYPE) - @Param(description = "the trafficType to be added to the physical network") + @Param(description = "The trafficType to be added to the physical Network") private String trafficType; @SerializedName(ApiConstants.PHYSICAL_NETWORK_ID) - @Param(description = "the physical network this belongs to") + @Param(description = "The physical Network this belongs to") private String physicalNetworkId; @SerializedName(ApiConstants.XENSERVER_NETWORK_LABEL) - @Param(description = "The network name label of the physical device dedicated to this traffic on a XenServer host") + @Param(description = "The Network name label of the physical device dedicated to this traffic on a XenServer host") private String xenNetworkLabel; @SerializedName(ApiConstants.KVM_NETWORK_LABEL) - @Param(description = "The network name label of the physical device dedicated to this traffic on a KVM host") + @Param(description = "The Network name label of the physical device dedicated to this traffic on a KVM host") private String kvmNetworkLabel; @SerializedName(ApiConstants.VMWARE_NETWORK_LABEL) - @Param(description = "The network name label of the physical device dedicated to this traffic on a VMware host") + @Param(description = "The Network name label of the physical device dedicated to this traffic on a VMware host") private String vmwareNetworkLabel; @SerializedName(ApiConstants.HYPERV_NETWORK_LABEL) - @Param(description = "The network name label of the physical device dedicated to this traffic on a HyperV host") + @Param(description = "The Network name label of the physical device dedicated to this traffic on a HyperV host") private String hypervNetworkLabel; + @SerializedName(ApiConstants.VLAN) + @Param(description = "The VLAN id to be used for Management traffic by VMware host") + private String vlan; + + @SerializedName(ApiConstants.ISOLATION_METHODS) + @Param(description = "isolation methods for the physical network traffic") + private String isolationMethods; + @SerializedName(ApiConstants.OVM3_NETWORK_LABEL) - @Param(description = "The network name of the physical device dedicated to this traffic on an OVM3 host") + @Param(description = "The Network name of the physical device dedicated to this traffic on an OVM3 host") private String ovm3NetworkLabel; @Override @@ -128,4 +136,20 @@ public String getOvm3Label() { public void setOvm3Label(String ovm3Label) { this.ovm3NetworkLabel = ovm3Label; } + + public String getIsolationMethods() { + return isolationMethods; + } + + public void setIsolationMethods(String isolationMethods) { + this.isolationMethods = isolationMethods; + } + + public String getVlan() { + return vlan; + } + + public void setVlan(String vlan) { + this.vlan = vlan; + } } 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 e9d45cb506ae..5b3f15b0ddce 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 @@ -25,13 +25,17 @@ public class UnmanageVMInstanceResponse extends BaseResponse { @SerializedName(ApiConstants.SUCCESS) - @Param(description = "result of the unmanage VM operation") + @Param(description = "Result of the unmanage Instance operation") private boolean success; @SerializedName(ApiConstants.DETAILS) - @Param(description = "details of the unmanage VM operation") + @Param(description = "Details of the unmanage Instance operation") private String details; + @SerializedName(ApiConstants.HOST_ID) + @Param(description = "The ID of the host used for unmanaged Instance") + private String hostId; + public UnmanageVMInstanceResponse() { } @@ -55,4 +59,12 @@ public String getDetails() { public void setDetails(String details) { this.details = details; } + + public String getHostId() { + return hostId; + } + + public void setHostId(String hostId) { + this.hostId = hostId; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UnmanagedInstanceDiskResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UnmanagedInstanceDiskResponse.java index 083c83fbf85a..4367164722b7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UnmanagedInstanceDiskResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UnmanagedInstanceDiskResponse.java @@ -26,47 +26,47 @@ public class UnmanagedInstanceDiskResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the disk") + @Param(description = "The ID of the disk") private String diskId; @SerializedName(ApiConstants.LABEL) - @Param(description = "the label of the disk") + @Param(description = "The label of the disk") private String label; @SerializedName(ApiConstants.CAPACITY) - @Param(description = "the capacity of the disk in bytes") + @Param(description = "The capacity of the disk in bytes") private Long capacity; @SerializedName(ApiConstants.IMAGE_PATH) - @Param(description = "the file path of the disk image") + @Param(description = "The file path of the disk image") private String imagePath; @SerializedName(ApiConstants.CONTROLLER) - @Param(description = "the controller of the disk") + @Param(description = "The controller of the disk") private String controller; @SerializedName(ApiConstants.CONTROLLER_UNIT) - @Param(description = "the controller unit of the disk") + @Param(description = "The controller unit of the disk") private Integer controllerUnit; @SerializedName(ApiConstants.POSITION) - @Param(description = "the position of the disk") + @Param(description = "The position of the disk") private Integer position; @SerializedName(ApiConstants.DATASTORE_NAME) - @Param(description = "the controller of the disk") + @Param(description = "The controller of the disk") private String datastoreName; @SerializedName(ApiConstants.DATASTORE_HOST) - @Param(description = "the controller of the disk") + @Param(description = "The controller of the disk") private String datastoreHost; @SerializedName(ApiConstants.DATASTORE_PATH) - @Param(description = "the controller of the disk") + @Param(description = "The controller of the disk") private String datastorePath; @SerializedName(ApiConstants.DATASTORE_TYPE) - @Param(description = "the controller of the disk") + @Param(description = "The controller of the disk") private String datastoreType; public String getDiskId() { 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 c1156f5f23a5..3f2dc045e87c 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 @@ -32,11 +32,11 @@ public class UnmanagedInstanceResponse extends BaseResponse { @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the virtual machine") + @Param(description = "The name of the Instance") private String name; @SerializedName(ApiConstants.CLUSTER_ID) - @Param(description = "the ID of the cluster to which virtual machine belongs") + @Param(description = "The ID of the cluster to which Instance belongs") private String clusterId; @SerializedName(ApiConstants.CLUSTER_NAME) @@ -44,39 +44,47 @@ public class UnmanagedInstanceResponse extends BaseResponse { private String clusterName; @SerializedName(ApiConstants.HOST_ID) - @Param(description = "the ID of the host to which virtual machine belongs") + @Param(description = "The ID of the host to which Instance belongs") private String hostId; @SerializedName(ApiConstants.HOST_NAME) - @Param(description = "the name of the host to which virtual machine belongs") + @Param(description = "The name of the host to which Instance belongs") private String hostName; + @SerializedName(ApiConstants.HYPERVISOR) + @Param(description = "The hypervisor to which Instance belongs") + private String hypervisor; + + @SerializedName(ApiConstants.HYPERVISOR_VERSION) + @Param(description = "The hypervisor version of the host to which Instance belongs") + private String hypervisorVersion; + @SerializedName(ApiConstants.POWER_STATE) - @Param(description = "the power state of the virtual machine") + @Param(description = "The power state of the Instance") private String powerState; @SerializedName(ApiConstants.CPU_NUMBER) - @Param(description = "the CPU cores of the virtual machine") + @Param(description = "The CPU cores of the Instance") private Integer cpuCores; @SerializedName(ApiConstants.CPU_CORE_PER_SOCKET) - @Param(description = "the CPU cores per socket for the virtual machine. VMware specific") + @Param(description = "The CPU cores per socket for the Instance. VMware specific") private Integer cpuCoresPerSocket; @SerializedName(ApiConstants.CPU_SPEED) - @Param(description = "the CPU speed of the virtual machine") + @Param(description = "The CPU speed of the Instance") private Integer cpuSpeed; @SerializedName(ApiConstants.MEMORY) - @Param(description = "the memory of the virtual machine in MB") + @Param(description = "The memory of the Instance in MB") private Integer memory; @SerializedName(ApiConstants.OS_ID) - @Param(description = "the operating system ID of the virtual machine") + @Param(description = "The operating system ID of the Instance") private String operatingSystemId; @SerializedName(ApiConstants.OS_DISPLAY_NAME) - @Param(description = "the operating system of the virtual machine") + @Param(description = "The operating system of the Instance") private String operatingSystem; @SerializedName(ApiConstants.BOOT_MODE) @@ -88,11 +96,11 @@ public class UnmanagedInstanceResponse extends BaseResponse { private String bootType; @SerializedName(ApiConstants.DISK) - @Param(description = "the list of disks associated with the virtual machine", responseObject = UnmanagedInstanceDiskResponse.class) + @Param(description = "The list of disks associated with the Instance", responseObject = UnmanagedInstanceDiskResponse.class) private Set disks; @SerializedName(ApiConstants.NIC) - @Param(description = "the list of nics associated with the virtual machine", responseObject = NicResponse.class) + @Param(description = "The list of NICs associated with the Instance", responseObject = NicResponse.class) private Set nics; public UnmanagedInstanceResponse() { @@ -140,6 +148,22 @@ public void setHostName(String hostName) { this.hostName = hostName; } + public String getHypervisor() { + return hypervisor; + } + + public void setHypervisor(String hypervisor) { + this.hypervisor = hypervisor; + } + + public String getHypervisorVersion() { + return hypervisorVersion; + } + + public void setHypervisorVersion(String hypervisorVersion) { + this.hypervisorVersion = hypervisorVersion; + } + public String getPowerState() { return powerState; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UpgradeRouterTemplateResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UpgradeRouterTemplateResponse.java index d89631ae4287..dbe990c6dbd3 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UpgradeRouterTemplateResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UpgradeRouterTemplateResponse.java @@ -16,27 +16,10 @@ // 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 org.apache.cloudstack.api.EntityReference; import org.apache.cloudstack.jobs.JobInfo; -import com.cloud.serializer.Param; - @EntityReference(value = JobInfo.class) -@SuppressWarnings("unused") public class UpgradeRouterTemplateResponse extends BaseResponse { - @SerializedName(ApiConstants.JOB_ID) - @Param(description = "the id of AsyncJob") - private String asyncJobId; - - public String getAsyncJobId() { - return asyncJobId; - } - - public void setAsyncJobId(String asyncJobId) { - this.asyncJobId = asyncJobId; - } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UsageRecordResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UsageRecordResponse.java index 4aeded642871..21b421e66bfd 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UsageRecordResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UsageRecordResponse.java @@ -29,119 +29,119 @@ @SuppressWarnings("unused") public class UsageRecordResponse extends BaseResponseWithTagInformation implements ControlledEntityResponse { @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the user account name") + @Param(description = "The user Account name") private String accountName; @SerializedName(ApiConstants.ACCOUNT_ID) - @Param(description = "the user account Id") + @Param(description = "The user Account ID") private String accountId; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the resource") + @Param(description = "The project ID of the resource") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the resource") + @Param(description = "The project name of the resource") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID") + @Param(description = "The domain ID") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain the resource is associated with") + @Param(description = "The domain the resource is associated with") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) - @Param(description = "path of the domain to which the usage reocrd belongs", since = "4.19.2.0") + @Param(description = "Path of the domain to which the usage reocrd belongs", since = "4.19.2.0") private String domainPath; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the zone ID") + @Param(description = "The zone ID") private String zoneId; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "description of the usage record") + @Param(description = "Description of the usage record") private String description; @SerializedName("usage") - @Param(description = "usage in hours") + @Param(description = "Usage in hours") private String usage; @SerializedName("usagetype") - @Param(description = "usage type ID") + @Param(description = "Usage type ID") private Integer usageType; @SerializedName("rawusage") - @Param(description = "raw usage in hours") + @Param(description = "Raw usage in hours") private String rawUsage; @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) - @Param(description = "virtual machine ID") + @Param(description = "Instance ID") private String virtualMachineId; @SerializedName(ApiConstants.NAME) - @Param(description = "resource or virtual machine name") + @Param(description = "Resource or Instance name") private String resourceName; @SerializedName("offeringid") - @Param(description = "offering ID") + @Param(description = "Offering ID") private String offeringId; @SerializedName(ApiConstants.TEMPLATE_ID) - @Param(description = "template ID") + @Param(description = "Template ID") private String templateId; @SerializedName(ApiConstants.OS_TYPE_ID) - @Param(description = "virtual machine os type ID") + @Param(description = "Instance os type ID") private String osTypeId; @SerializedName(ApiConstants.OS_DISPLAY_NAME) - @Param(description = "virtual machine os display name") + @Param(description = "Instance os display name") private String osDisplayName; @SerializedName(ApiConstants.OS_CATEGORY_ID) - @Param(description = "virtual machine guest os category ID") + @Param(description = "Instance guest os category ID") private String osCategoryId; @SerializedName(ApiConstants.OS_CATEGORY_NAME) - @Param(description = "virtual machine os category name") + @Param(description = "Instance os category name") private String osCategoryName; @SerializedName("usageid") - @Param(description = "id of the resource") + @Param(description = "ID of the resource") private String usageId; @SerializedName(ApiConstants.TYPE) - @Param(description = "resource type") + @Param(description = "Resource type") private String type; @SerializedName(ApiConstants.SIZE) - @Param(description = "resource size") + @Param(description = "Resource size") private Long size; @SerializedName("virtualsize") - @Param(description = "virtual size of resource") + @Param(description = "Virtual size of resource") private Long virtualSize; @SerializedName(ApiConstants.CPU_NUMBER) - @Param(description = "number of cpu of resource") + @Param(description = "Number of CPU of resource") private Long cpuNumber; @SerializedName(ApiConstants.CPU_SPEED) - @Param(description = "speed of each cpu of resource") + @Param(description = "Speed of each CPU of resource") private Long cpuSpeed; @SerializedName(ApiConstants.MEMORY) - @Param(description = "memory allocated for the resource") + @Param(description = "Memory allocated for the resource") private Long memory; @SerializedName(ApiConstants.START_DATE) - @Param(description = "start date of the usage record") + @Param(description = "Start date of the usage record") private Date startDate; @SerializedName(ApiConstants.END_DATE) - @Param(description = "end date of the usage record") + @Param(description = "End date of the usage record") private Date endDate; @SerializedName("issourcenat") @@ -149,11 +149,11 @@ public class UsageRecordResponse extends BaseResponseWithTagInformation implemen private Boolean isSourceNat; @SerializedName(ApiConstants.IS_SYSTEM) - @Param(description = "True if the IPAddress is system IP - allocated during vm deploy or lb rule create") + @Param(description = "True if the IPAddress is system IP - allocated during Instance deploy or LB rule create") private Boolean isSystem; @SerializedName("networkid") - @Param(description = "id of the network") + @Param(description = "ID of the Network") private String networkId; @SerializedName("isdefault") @@ -161,7 +161,7 @@ public class UsageRecordResponse extends BaseResponseWithTagInformation implemen private Boolean isDefault; @SerializedName("vpcid") - @Param(description = "id of the vpc") + @Param(description = "ID of the VPC") private String vpcId; public UsageRecordResponse() { 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 ce344596aebc..5871ae6ee7a2 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 @@ -34,34 +34,34 @@ public class UserDataResponse extends BaseResponseWithAnnotations implements Con @Param(description = "Name of the User Data") private String name; - @SerializedName(ApiConstants.ACCOUNT_ID) @Param(description="the owner id of the User Data") + @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 User Data") + @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 User Data", 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 User Data", 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 User Data 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 User Data 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 User Data 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 User Data 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 the User Data") + @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/UserResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UserResponse.java index 5e4e6e1f3c8b..61b025b206eb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UserResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UserResponse.java @@ -32,88 +32,88 @@ @EntityReference(value = User.class) public class UserResponse extends BaseResponse implements SetResourceIconResponse { @SerializedName("id") - @Param(description = "the user ID") + @Param(description = "The user ID") private String id; @SerializedName("username") - @Param(description = "the user name") + @Param(description = "The user name") private String username; @SerializedName("firstname") - @Param(description = "the user firstname") + @Param(description = "The user firstname") private String firstname; @SerializedName("lastname") - @Param(description = "the user lastname") + @Param(description = "The user lastname") private String lastname; @SerializedName("email") - @Param(description = "the user email address") + @Param(description = "The user email address") private String email; @SerializedName("created") - @Param(description = "the date and time the user account was created") + @Param(description = "The date and time the user Account was created") private Date created; @SerializedName("state") - @Param(description = "the user state") + @Param(description = "The user state") private String state; @SerializedName("account") - @Param(description = "the account name of the user") + @Param(description = "The Account name of the user") private String accountName; @SerializedName("accounttype") - @Param(description = "the account type of the user") + @Param(description = "The Account type of the user") private Integer accountType; @SerializedName(ApiConstants.USER_SOURCE) - @Param(description = "the source type of the user in lowercase, such as native, ldap, saml2") + @Param(description = "The source type of the user in lowercase, such as native, ldap, saml2") private String userSource; @SerializedName(ApiConstants.ROLE_ID) - @Param(description = "the ID of the role") + @Param(description = "The ID of the role") private String roleId; @SerializedName(ApiConstants.ROLE_TYPE) - @Param(description = "the type of the role") + @Param(description = "The type of the role") private String roleType; @SerializedName(ApiConstants.ROLE_NAME) - @Param(description = "the name of the role") + @Param(description = "The name of the role") private String roleName; @SerializedName("domainid") - @Param(description = "the domain ID of the user") + @Param(description = "The domain ID of the user") private String domainId; @SerializedName("domain") - @Param(description = "the domain name of the user") + @Param(description = "The domain name of the user") private String domainName; @SerializedName("timezone") - @Param(description = "the timezone user was created in") + @Param(description = "The timezone user was created in") private String timezone; - @SerializedName("apikey") - @Param(description = "the api key of the user", isSensitive = true) + @SerializedName(ApiConstants.API_KEY) + @Param(description = "The API key of the user", isSensitive = true) private String apiKey; @Deprecated - @SerializedName("secretkey") - @Param(description = "the secret key of the user", isSensitive = true) + @SerializedName(ApiConstants.SECRET_KEY) + @Param(description = "The secret key of the user", isSensitive = true) private String secretKey; @SerializedName("accountid") - @Param(description = "the account ID of the user") + @Param(description = "The Account ID of the user") private String accountId; @SerializedName("iscallerchilddomain") - @Param(description = "the boolean value representing if the updating target is in caller's child domain") + @Param(description = "The boolean value representing if the updating target is in caller's child domain") private boolean isCallerChildDomain; @SerializedName(ApiConstants.IS_DEFAULT) - @Param(description = "true if user is default, false otherwise", since = "4.2.0") + @Param(description = "True if user is default, false otherwise", since = "4.2.0") private Boolean isDefault; @SerializedName(ApiConstants.RESOURCE_ICON) @@ -121,15 +121,15 @@ public class UserResponse extends BaseResponse implements SetResourceIconRespons ResourceIconResponse icon; @SerializedName(ApiConstants.IS_2FA_ENABLED) - @Param(description = "true if user has two factor authentication enabled", since = "4.18.0.0") + @Param(description = "True if user has two factor authentication enabled", since = "4.18.0.0") private Boolean is2FAenabled; @SerializedName(ApiConstants.IS_2FA_MANDATED) - @Param(description = "true if user has two factor authentication is mandated", since = "4.18.0.0") + @Param(description = "True if user has two factor authentication is mandated", since = "4.18.0.0") private Boolean is2FAmandated; @SerializedName(ApiConstants.API_KEY_ACCESS) - @Param(description = "whether api key access is Enabled, Disabled or set to Inherit (it inherits the value from the parent)", since = "4.20.1.0") + @Param(description = "Whether api key access is Enabled, Disabled or set to Inherit (it inherits the value from the parent)", since = "4.20.1.0") ApiConstants.ApiKeyAccess apiKeyAccess; @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UserTwoFactorAuthenticationSetupResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UserTwoFactorAuthenticationSetupResponse.java index 35beefde4031..43118a4c474b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UserTwoFactorAuthenticationSetupResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UserTwoFactorAuthenticationSetupResponse.java @@ -26,19 +26,19 @@ public class UserTwoFactorAuthenticationSetupResponse extends BaseResponse { @SerializedName("id") - @Param(description = "the user ID") + @Param(description = "The user ID") private String id; @SerializedName("username") - @Param(description = "the user name") + @Param(description = "The user name") private String username; @SerializedName("accountid") - @Param(description = "the account ID of the user") + @Param(description = "The Account ID of the user") private String accountId; @SerializedName(ApiConstants.SECRET_CODE) - @Param(description = "secret code that needs to be registered with authenticator") + @Param(description = "Secret code that needs to be registered with authenticator") private String secretCode; public void setId(String id) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UserTwoFactorAuthenticatorProviderResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UserTwoFactorAuthenticatorProviderResponse.java index 4101dc375ed1..7b0057d66ea6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UserTwoFactorAuthenticatorProviderResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UserTwoFactorAuthenticatorProviderResponse.java @@ -27,11 +27,11 @@ public class UserTwoFactorAuthenticatorProviderResponse extends BaseResponse { @SerializedName(ApiConstants.NAME) - @Param(description = "the user two factor authenticator provider name") + @Param(description = "The user two factor authenticator provider name") private String name; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "the description of the user two factor authenticator provider") + @Param(description = "The description of the user two factor authenticator provider") private String description; public String getName() { 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 ca5bd09a9aa8..e08c6019f150 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 @@ -41,145 +41,153 @@ @SuppressWarnings("unused") @EntityReference(value = {VirtualMachine.class, UserVm.class, VirtualRouter.class}) -public class UserVmResponse extends BaseResponseWithTagInformation implements ControlledEntityResponse, SetResourceIconResponse { +public class UserVmResponse extends BaseResponseWithTagInformation implements ControlledViewEntityResponse, SetResourceIconResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the virtual machine") + @Param(description = "The ID of the Instance") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the virtual machine") + @Param(description = "The name of the Instance") private String name; @SerializedName("displayname") - @Param(description = "user generated name. The name of the virtual machine is returned if no displayname exists.") + @Param(description = "User generated name. The name of the Instance is returned if no displayname exists.") private String displayName; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account associated with the virtual machine") + @Param(description = "The Account associated with the Instance") private String accountName; @SerializedName(ApiConstants.USER_ID) - @Param(description = "the user's ID who deployed the virtual machine") + @Param(description = "The User's ID who deployed the Instance") private String userId; @SerializedName(ApiConstants.USERNAME) - @Param(description = "the user's name who deployed the virtual machine") + @Param(description = "The User's name who deployed the Instance") private String userName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the vm") + @Param(description = "The project ID of the Instance") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the vm") + @Param(description = "The project name of the Instance") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the ID of the domain in which the virtual machine exists") + @Param(description = "The ID of the domain in which the Instance exists") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the name of the domain in which the virtual machine exists") + @Param(description = "The name of the domain in which the Instance exists") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) - @Param(description = "path of the domain in which the virtual machine exists", since = "4.19.2.0") + @Param(description = "Path of the domain in which the virtual machine exists", since = "4.19.2.0") private String domainPath; @SerializedName(ApiConstants.CREATED) - @Param(description = "the date when this virtual machine was created") + @Param(description = "The date when this Instance was created") private Date created; @SerializedName("lastupdated") - @Param(description="the date when this virtual machine was updated last time", since="4.16.0") + @Param(description = "The date when this Instance was updated last time", since="4.16.0") private Date lastUpdated; @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the virtual machine") + @Param(description = "The state of the Instance") private String state; @SerializedName(ApiConstants.HA_ENABLE) - @Param(description = "true if high-availability is enabled, false otherwise") + @Param(description = "True if high-availability is enabled, false otherwise") private Boolean haEnable; @SerializedName(ApiConstants.GROUP_ID) - @Param(description = "the group ID of the virtual machine") + @Param(description = "The group ID of the Instance") private String groupId; @SerializedName(ApiConstants.GROUP) - @Param(description = "the group name of the virtual machine") + @Param(description = "The group name of the Instance") private String group; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the ID of the availability zone for the virtual machine") + @Param(description = "The ID of the availability zone for the Instance") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "the name of the availability zone for the virtual machine") + @Param(description = "The name of the availability zone for the Instance") private String zoneName; @SerializedName(ApiConstants.HOST_ID) - @Param(description = "the ID of the host for the virtual machine") + @Param(description = "The ID of the host for the Instance") private String hostId; @SerializedName("hostname") - @Param(description = "the name of the host for the virtual machine") + @Param(description = "The name of the host for the Instance") private String hostName; @SerializedName(ApiConstants.HOST_CONTROL_STATE) - @Param(description = "the control state of the host for the virtual machine") + @Param(description = "The control state of the host for the Instance") private String hostControlState; @SerializedName(ApiConstants.TEMPLATE_ID) - @Param(description = "the ID of the template for the virtual machine. A -1 is returned if the virtual machine was created from an ISO file.") + @Param(description = "The ID of the Template for the Instance. A -1 is returned if the Instance was created from an ISO file.") private String templateId; @SerializedName("templatename") - @Param(description = "the name of the template for the virtual machine") + @Param(description = "The name of the Template for the Instance") private String templateName; @SerializedName(ApiConstants.TEMPLATE_TYPE) - @Param(description = "the type of the template for the virtual machine", since = "4.19.0") + @Param(description = "The type of the template for the virtual machine", since = "4.19.0") private String templateType; @SerializedName(ApiConstants.TEMPLATE_FORMAT) - @Param(description = "the format of the template for the virtual machine", since = "4.19.1") + @Param(description = "The format of the template for the virtual machine", since = "4.19.1") private String templateFormat; @SerializedName("templatedisplaytext") - @Param(description = " an alternate display text of the template for the virtual machine") + @Param(description = "An alternate display text of the Template for the Instance") private String templateDisplayText; @SerializedName(ApiConstants.PASSWORD_ENABLED) - @Param(description = "true if the password rest feature is enabled, false otherwise") + @Param(description = "True if the password rest feature is enabled, false otherwise") private Boolean passwordEnabled; @SerializedName("isoid") - @Param(description = "the ID of the ISO attached to the virtual machine") + @Param(description = "The ID of the ISO attached to the Instance") private String isoId; @SerializedName("isoname") - @Param(description = "the name of the ISO attached to the virtual machine") + @Param(description = "The name of the ISO attached to the Instance") private String isoName; @SerializedName("isodisplaytext") - @Param(description = "an alternate display text of the ISO attached to the virtual machine") + @Param(description = "An alternate display text of the ISO attached to the Instance") private String isoDisplayText; + @SerializedName("isos") + @Param(description = "All ISOs attached to the Instance, keyed by cdrom slot. The first entry mirrors isoid/isoname for back-compat.", responseObject = AttachedIsoResponse.class, since = "4.23.0") + private List isos; + + @SerializedName("isomaxcount") + @Param(description = "Maximum number of ISOs that may be attached to this Instance, after applying the cluster-scoped vm.iso.max.count and the hypervisor's own cap.", since = "4.23.0") + private Integer isoMaxCount; + @SerializedName(ApiConstants.SERVICE_OFFERING_ID) - @Param(description = "the ID of the service offering of the virtual machine") + @Param(description = "The ID of the service offering of the Instance") private String serviceOfferingId; @SerializedName("serviceofferingname") - @Param(description = "the name of the service offering of the virtual machine") + @Param(description = "The name of the service offering of the Instance") private String serviceOfferingName; @SerializedName(ApiConstants.DISK_OFFERING_ID) - @Param(description = "the ID 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") + @Param(description = "The ID of the disk offering of the Instance. This parameter should not be used for retrieving disk offering details of DATA volumes. Use listVolumes API instead", since = "4.4") private String diskOfferingId; @SerializedName("diskofferingname") - @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") + @Param(description = "The name of the disk offering of the Instance. 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) @@ -219,99 +227,103 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co private Integer gpuCount; @SerializedName(ApiConstants.BACKUP_OFFERING_ID) - @Param(description = "the ID of the backup offering of the virtual machine", since = "4.14") + @Param(description = "The ID of the backup offering of the Instance", since = "4.14") private String backupOfferingId; @SerializedName(ApiConstants.BACKUP_OFFERING_NAME) - @Param(description = "the name of the backup offering of the virtual machine", since = "4.14") + @Param(description = "The name of the backup offering of the Instance", since = "4.14") private String backupOfferingName; + @SerializedName(ApiConstants.BACKUP_PROVIDER) + @Param(description = "The name of the backup provider of the offering attached to the Instance", since = "4.23.0") + private String backupProvider; + @SerializedName("forvirtualnetwork") - @Param(description = "the virtual network for the service offering") + @Param(description = "The virtual Network for the service offering") private Boolean forVirtualNetwork; @SerializedName(ApiConstants.CPU_NUMBER) - @Param(description = "the number of vCPUs this virtual machine is using") + @Param(description = "The number of vCPUs this Instance is using") private Integer cpuNumber; @SerializedName(ApiConstants.CPU_SPEED) - @Param(description = "the speed of each vCPU") + @Param(description = "The speed of each vCPU") private Integer cpuSpeed; @SerializedName(ApiConstants.MEMORY) - @Param(description = "the memory allocated for the virtual machine") + @Param(description = "The memory allocated for the Instance") private Integer memory; @SerializedName(ApiConstants.VGPU) - @Param(description = "the vGPU type used by the virtual machine", since = "4.4") + @Param(description = "The vGPU type used by the Instance", since = "4.4") private String vgpu; @SerializedName("cpuused") - @Param(description = "the amount of the vm's CPU currently used") + @Param(description = "The amount of the Instance's CPU currently used") private String cpuUsed; @SerializedName("networkkbsread") - @Param(description = "the incoming network traffic on the VM in KiB") + @Param(description = "The incoming Network traffic on the Instance in KiB") private Long networkKbsRead; @SerializedName("networkkbswrite") - @Param(description = "the outgoing network traffic on the host in KiB") + @Param(description = "The outgoing Network traffic on the host in KiB") private Long networkKbsWrite; @SerializedName(ApiConstants.DISK_KBS_READ) - @Param(description = "the VM's disk read in KiB") + @Param(description = "The Instance's disk read in KiB") private Long diskKbsRead; @SerializedName(ApiConstants.DISK_KBS_WRITE) - @Param(description = "the VM's disk write in KiB") + @Param(description = "The Instance's disk write in KiB") private Long diskKbsWrite; @SerializedName("memorykbs") - @Param(description = "the memory used by the VM in KiB") + @Param(description = "The memory used by the Instance in KiB") private Long memoryKBs; @SerializedName("memoryintfreekbs") - @Param(description = "the internal memory (KiB) that's free in VM or zero if it can not be calculated") + @Param(description = "The internal memory (KiB) that's free in Instance or zero if it can not be calculated") private Long memoryIntFreeKBs; @SerializedName("memorytargetkbs") - @Param(description = "the target memory in VM (KiB)") + @Param(description = "The target memory in Instance (KiB)") private Long memoryTargetKBs; @SerializedName(ApiConstants.DISK_IO_READ) - @Param(description = "the read (IO) of disk on the VM") + @Param(description = "The read (IO) of disk on the Instance") private Long diskIORead; @SerializedName(ApiConstants.DISK_IO_WRITE) - @Param(description = "the write (IO) of disk on the VM") + @Param(description = "The write (IO) of disk on the Instance") private Long diskIOWrite; @SerializedName("guestosid") - @Param(description = "Os type ID of the virtual machine") + @Param(description = "OS type ID of the Instance") private String guestOsId; @SerializedName("rootdeviceid") - @Param(description = "device ID of the root volume") + @Param(description = "Device ID of the root volume") private Long rootDeviceId; @SerializedName("rootdevicetype") - @Param(description = "device type of the root volume") + @Param(description = "Device type of the root volume") private String rootDeviceType; @SerializedName("securitygroup") - @Param(description = "list of security groups associated with the virtual machine", responseObject = SecurityGroupResponse.class) + @Param(description = "List of security groups associated with the Instance", responseObject = SecurityGroupResponse.class) private Set securityGroupList; @SerializedName(ApiConstants.PASSWORD) - @Param(description = "the password (if exists) of the virtual machine", isSensitive = true) + @Param(description = "The password (if exists) of the Instance", isSensitive = true) private String password; @SerializedName("nic") - @Param(description = "the list of nics associated with vm", responseObject = NicResponse.class) + @Param(description = "The list of NICs associated with Instance", responseObject = NicResponse.class) private Set nics; @SerializedName("hypervisor") - @Param(description = "the hypervisor on which the template runs") + @Param(description = "The hypervisor on which the Template runs") private String hypervisor; @SerializedName(ApiConstants.IP_ADDRESS) @@ -319,41 +331,45 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co private String ipAddress; @SerializedName(ApiConstants.PUBLIC_IP_ID) - @Param(description = "public IP address id associated with vm via Static nat rule") + @Param(description = "Public IP address id associated with Instance via Static NAT rule") private String publicIpId; @SerializedName(ApiConstants.PUBLIC_IP) - @Param(description = "public IP address id associated with vm via Static nat rule") + @Param(description = "Public IP address id associated with Instance via Static NAT rule") private String publicIp; @SerializedName(ApiConstants.INSTANCE_NAME) - @Param(description = "instance name of the user vm; this parameter is returned to the ROOT admin only", since = "3.0.1") + @Param(description = "Instance name of the user Instance; this parameter is returned to the ROOT admin only", since = "3.0.1") private String instanceName; transient Set tagIds; @SerializedName(ApiConstants.DETAILS) - @Param(description = "Vm details in key/value pairs.", since = "4.2.1") + @Param(description = "Instance details in key/value pairs.", since = "4.2.1") private Map details; @SerializedName("readonlydetails") - @Param(description = "List of read-only Vm details as comma separated string.", since = "4.16.0") + @Param(description = "List of read-only Instance details as comma separated string.", since = "4.16.0") private String readOnlyDetails; + @SerializedName("alloweddetails") + @Param(description = "List of allowed Vm details as comma separated string if VM instance settings are read from OVA.", since = "4.22.1") + private String allowedDetails; + @SerializedName(ApiConstants.SSH_KEYPAIRS) - @Param(description = "ssh key-pairs") + @Param(description = "SSH key-pairs") private String keyPairNames; @SerializedName("affinitygroup") - @Param(description = "list of affinity groups associated with the virtual machine", responseObject = AffinityGroupResponse.class) + @Param(description = "List of Affinity groups associated with the Instance", responseObject = AffinityGroupResponse.class) private Set affinityGroupList; @SerializedName(ApiConstants.DISPLAY_VM) - @Param(description = "an optional field whether to the display the vm to the end user or not.", authorized = {RoleType.Admin}) + @Param(description = "An optional field whether to the display the Instance to the end user or not.", authorized = {RoleType.Admin}) private Boolean displayVm; @SerializedName(ApiConstants.IS_DYNAMICALLY_SCALABLE) - @Param(description = "true if vm contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory.") + @Param(description = "True if Instance contains XS/VMWare tools in order to support dynamic scaling of Instance CPU/memory.") private Boolean isDynamicallyScalable; @SerializedName(ApiConstants.DELETE_PROTECTION) @@ -365,31 +381,31 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co private String serviceState; @SerializedName(ApiConstants.OS_TYPE_ID) - @Param(description = "OS type id of the vm", since = "4.4") + @Param(description = "OS type id of the Instance", since = "4.4") private String osTypeId; @SerializedName(ApiConstants.OS_DISPLAY_NAME) - @Param(description = "OS name of the vm", since = "4.13.2") + @Param(description = "OS name of the Instance", since = "4.13.2") private String osDisplayName; @SerializedName(ApiConstants.BOOT_MODE) - @Param(description = "Guest vm Boot Mode") + @Param(description = "Guest Instance Boot Mode") private String bootMode; @SerializedName(ApiConstants.BOOT_TYPE) - @Param(description = "Guest vm Boot Type") + @Param(description = "Guest Instance Boot Type") private String bootType; @SerializedName(ApiConstants.POOL_TYPE) - @Param(description = "the pool type of the virtual machine", since = "4.16") + @Param(description = "The pool type of the Instance", since = "4.16") private String poolType; @SerializedName(ApiConstants.RECEIVED_BYTES) - @Param(description = "the total number of network traffic bytes received") + @Param(description = "The total number of Network traffic bytes received") private Long bytesReceived; @SerializedName(ApiConstants.SENT_BYTES) - @Param(description = "the total number of network traffic bytes sent") + @Param(description = "The total number of Network traffic bytes sent") private Long bytesSent; @SerializedName(ApiConstants.RESOURCE_ICON) @@ -397,27 +413,27 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co ResourceIconResponse resourceIconResponse; @SerializedName(ApiConstants.AUTOSCALE_VMGROUP_ID) - @Param(description = "ID of AutoScale VM group", since = "4.18.0") + @Param(description = "ID of AutoScale Instance group", since = "4.18.0") String autoScaleVmGroupId; @SerializedName(ApiConstants.AUTOSCALE_VMGROUP_NAME) - @Param(description = "Name of AutoScale VM group", since = "4.18.0") + @Param(description = "Name of AutoScale Instance group", since = "4.18.0") String autoScaleVmGroupName; @SerializedName(ApiConstants.USER_DATA) @Param(description = "Base64 string containing the user data", since = "4.18.0.0") private String userData; - @SerializedName(ApiConstants.USER_DATA_ID) @Param(description="the id of userdata used for the VM", since = "4.18.0") + @SerializedName(ApiConstants.USER_DATA_ID) @Param(description = "The ID of userdata used for the Instance", since = "4.18.0") private String userDataId; - @SerializedName(ApiConstants.USER_DATA_NAME) @Param(description="the name of userdata used for the VM", since = "4.18.0") + @SerializedName(ApiConstants.USER_DATA_NAME) @Param(description = "The name of userdata used for the Instance", since = "4.18.0") private String userDataName; - @SerializedName(ApiConstants.USER_DATA_POLICY) @Param(description="the userdata override policy with the userdata provided while deploying VM", since = "4.18.0") + @SerializedName(ApiConstants.USER_DATA_POLICY) @Param(description = "The userdata override policy with the userdata provided while deploying Instance", since = "4.18.0") private String userDataPolicy; - @SerializedName(ApiConstants.USER_DATA_DETAILS) @Param(description="list of variables and values for the variables declared in userdata", since = "4.18.0") + @SerializedName(ApiConstants.USER_DATA_DETAILS) @Param(description = "List of variables and values for the variables declared in userdata", since = "4.18.0") private String userDataDetails; @SerializedName(ApiConstants.VNF_NICS) @@ -867,6 +883,22 @@ public void setIsoId(String isoId) { this.isoId = isoId; } + public void setIsos(List isos) { + this.isos = isos; + } + + public List getIsos() { + return isos; + } + + public void setIsoMaxCount(Integer isoMaxCount) { + this.isoMaxCount = isoMaxCount; + } + + public Integer getIsoMaxCount() { + return isoMaxCount; + } + public void setIsoName(String isoName) { this.isoName = isoName; } @@ -1091,6 +1123,10 @@ public void setReadOnlyDetails(String readOnlyDetails) { this.readOnlyDetails = readOnlyDetails; } + public void setAllowedDetails(String allowedDetails) { + this.allowedDetails = allowedDetails; + } + public void setOsTypeId(String osTypeId) { this.osTypeId = osTypeId; } @@ -1115,6 +1151,10 @@ public String getReadOnlyDetails() { return readOnlyDetails; } + public String getAllowedDetails() { + return allowedDetails; + } + public Boolean getDynamicallyScalable() { return isDynamicallyScalable; } @@ -1326,4 +1366,11 @@ public void setLeaseExpiryDate(Date leaseExpiryDate) { this.leaseExpiryDate = leaseExpiryDate; } + public String getBackupProvider() { + return backupProvider; + } + + public void setBackupProvider(String backupProvider) { + this.backupProvider = backupProvider; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VMScheduleResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VMScheduleResponse.java index 813a48e572c5..34455b3a0bf4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VMScheduleResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VMScheduleResponse.java @@ -23,26 +23,26 @@ import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; -import org.apache.cloudstack.vm.schedule.VMSchedule; +import org.apache.cloudstack.schedule.ResourceSchedule; import java.util.Date; -@EntityReference(value = VMSchedule.class) +@EntityReference(value = ResourceSchedule.class) public class VMScheduleResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of VM schedule") + @Param(description = "The ID of Instance schedule") private String id; @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) - @Param(description = "ID of virtual machine") + @Param(description = "ID of Instance") private String vmId; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "Description of VM schedule") + @Param(description = "Description of Instance schedule") private String description; @SerializedName(ApiConstants.SCHEDULE) - @Param(description = "Cron formatted VM schedule") + @Param(description = "Cron formatted Instance schedule") private String schedule; @SerializedName(ApiConstants.TIMEZONE) @@ -51,7 +51,7 @@ public class VMScheduleResponse extends BaseResponse { @SerializedName(ApiConstants.ACTION) @Param(description = "Action") - private VMSchedule.Action action; + private ResourceSchedule.Action action; @SerializedName(ApiConstants.ENABLED) @Param(description = "VM schedule is enabled") @@ -69,6 +69,20 @@ public class VMScheduleResponse extends BaseResponse { @Param(description = "Date when the schedule was created") private Date created; + public VMScheduleResponse(ResourceScheduleResponse scheduleResponse) { + super("vmschedule"); + this.id = scheduleResponse.getId(); + this.vmId = scheduleResponse.getResourceId(); + this.description = scheduleResponse.getDescription(); + this.schedule = scheduleResponse.getSchedule(); + this.timeZone = scheduleResponse.getTimeZone(); + this.action = scheduleResponse.getAction(); + this.enabled = scheduleResponse.getEnabled(); + this.startDate = scheduleResponse.getStartDate(); + this.endDate = scheduleResponse.getEndDate(); + this.created = scheduleResponse.getCreated(); + } + public void setId(String id) { this.id = id; } @@ -89,7 +103,7 @@ public void setTimeZone(String timeZone) { this.timeZone = timeZone; } - public void setAction(VMSchedule.Action action) { + public void setAction(ResourceSchedule.Action action) { this.action = action; } @@ -105,5 +119,7 @@ public void setEndDate(Date endDate) { this.endDate = endDate; } - public void setCreated(Date created) {this.created = created;} + public void setCreated(Date created) { + this.created = created; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VMSnapshotResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VMSnapshotResponse.java index 703f27b537c2..46d5c548eb0f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VMSnapshotResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VMSnapshotResponse.java @@ -33,87 +33,87 @@ public class VMSnapshotResponse extends BaseResponseWithTagInformation implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the vm snapshot") + @Param(description = "The ID of the Instance Snapshot") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the vm snapshot") + @Param(description = "The name of the Instance Snapshot") private String name; @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the vm snapshot") + @Param(description = "The state of the Instance Snapshot") private VMSnapshot.State state; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "the description of the vm snapshot") + @Param(description = "The description of the Instance Snapshot") private String description; @SerializedName(ApiConstants.DISPLAY_NAME) - @Param(description = "the display name of the vm snapshot") + @Param(description = "The display name of the Instance Snapshot") private String displayName; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the Zone ID of the vm snapshot") + @Param(description = "The Zone ID of the Instance Snapshot") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "the Zone name of the vm snapshot", since = "4.15.1") + @Param(description = "The Zone name of the Instance Snapshot", since = "4.15.1") private String zoneName; @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) - @Param(description = "the vm ID of the vm snapshot") + @Param(description = "The Instance ID of the Instance Snapshot") private String virtualMachineId; @SerializedName(ApiConstants.VIRTUAL_MACHINE_NAME) - @Param(description = "the vm name of the vm snapshot", since = "4.15.1") + @Param(description = "The Instance name of the Instance Snapshot", since = "4.15.1") private String virtualMachineName; @SerializedName("parent") - @Param(description = "the parent ID of the vm snapshot") + @Param(description = "The parent ID of the Instance Snapshot") private String parent; @SerializedName("parentName") - @Param(description = "the parent displayName of the vm snapshot") + @Param(description = "The parent displayName of the Instance Snapshot") private String parentName; @SerializedName("current") - @Param(description = "indicates if this is current snapshot") + @Param(description = "Indicates if this is current Snapshot") private Boolean current; @SerializedName("type") - @Param(description = "VM Snapshot type") + @Param(description = "Instance Snapshot type") private String type; @SerializedName(ApiConstants.CREATED) - @Param(description = "the create date of the vm snapshot") + @Param(description = "The create date of the Instance Snapshot") private Date created; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account associated with the disk volume") + @Param(description = "The Account associated with the disk volume") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the vpn") + @Param(description = "The project ID of the VPN") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the vpn") + @Param(description = "The project name of the VPN") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the ID of the domain associated with the disk volume") + @Param(description = "The ID of the domain associated with the disk volume") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain associated with the disk volume") + @Param(description = "The domain associated with the disk volume") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) - @Param(description = "path of the domain to which the disk volume belongs", since = "4.19.2.0") + @Param(description = "Path of the domain to which the disk volume belongs", since = "4.19.2.0") private String domainPath; @SerializedName(ApiConstants.HYPERVISOR) - @Param(description = "the type of hypervisor on which snapshot is stored") + @Param(description = "The type of hypervisor on which Snapshot is stored") private String hypervisor; public VMSnapshotResponse() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VMUserDataResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VMUserDataResponse.java index cf819491c2c0..4ff7891a8e27 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VMUserDataResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VMUserDataResponse.java @@ -26,11 +26,11 @@ public class VMUserDataResponse extends BaseResponse { @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) - @Param(description = "the ID of the virtual machine") + @Param(description = "The ID of the Instance") private String vmId; @SerializedName(ApiConstants.USER_DATA) - @Param(description = "Base64 encoded VM user data") + @Param(description = "Base64 encoded Instance User Data") private String userData; public void setUserData(String userData) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VgpuResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VgpuResponse.java index c51dc8d0f667..bf0968dd9bf1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VgpuResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VgpuResponse.java @@ -46,15 +46,15 @@ public class VgpuResponse extends BaseResponse { private Long maxResolutionY; @SerializedName(ApiConstants.MAXVGPUPERPGPU) - @Param(description = "Maximum no. of vgpu per gpu card (pgpu)") + @Param(description = "Maximum no. of vGPU per GPU card (pgpu)") private Long maxVgpuPerPgpu; @SerializedName(ApiConstants.REMAININGCAPACITY) - @Param(description = "Remaining capacity in terms of no. of more VMs that can be deployped with this vGPU type") + @Param(description = "Remaining capacity in terms of no. of more Instances that can be deployed with this vGPU type") private Long remainingCapacity; @SerializedName(ApiConstants.MAXCAPACITY) - @Param(description = "Maximum vgpu can be created with this vgpu type on the given gpu group") + @Param(description = "Maximum vGPU can be created with this vGPU type on the given GPU group") private Long maxCapacity; public void setName(String name) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VirtualRouterProviderResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VirtualRouterProviderResponse.java index b9d18e238104..6d6be1677e45 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VirtualRouterProviderResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VirtualRouterProviderResponse.java @@ -29,11 +29,11 @@ @SuppressWarnings("unused") public class VirtualRouterProviderResponse extends BaseResponse implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the id of the router") + @Param(description = "The ID of the router") private String id; @SerializedName(ApiConstants.NSP_ID) - @Param(description = "the physical network service provider id of the provider") + @Param(description = "The physical Network service provider ID of the provider") private String nspId; @SerializedName(ApiConstants.ENABLED) @@ -41,27 +41,27 @@ public class VirtualRouterProviderResponse extends BaseResponse implements Contr private Boolean enabled; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account associated with the provider") + @Param(description = "The Account associated with the provider") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the ipaddress") + @Param(description = "The project ID of the IP address") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the address") + @Param(description = "The project name of the address") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID associated with the provider") + @Param(description = "The domain ID associated with the provider") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain associated with the provider") + @Param(description = "The domain associated with the provider") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) - @Param(description = "path of the domain to which the provider belongs", since = "4.19.2.0") + @Param(description = "Path of the domain to which the provider belongs", since = "4.19.2.0") private String domainPath; @Override 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 2be08bb7fcbb..9a171ef96ffc 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 @@ -28,103 +28,103 @@ @SuppressWarnings("unused") public class VlanIpRangeResponse extends BaseResponse implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the VLAN IP range") + @Param(description = "The ID of the VLAN IP range") private String id; @SerializedName("forvirtualnetwork") - @Param(description = "the virtual network for the VLAN IP range") + @Param(description = "The virtual Network for the VLAN IP range") private Boolean forVirtualNetwork; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the Zone ID of the VLAN IP range") + @Param(description = "The Zone ID of the VLAN IP range") private String zoneId; @SerializedName(ApiConstants.VLAN) - @Param(description = "the ID or VID of the VLAN.") + @Param(description = "The ID or VID of the VLAN.") private String vlan; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account of the VLAN IP range") + @Param(description = "The Account of the VLAN IP range") private String accountName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID of the VLAN IP range") + @Param(description = "The domain ID of the VLAN IP range") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name of the VLAN IP range") + @Param(description = "The domain name of the VLAN IP range") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) - @Param(description = "path of the domain to which the VLAN IP range belongs", since = "4.19.2.0") + @Param(description = "Path of the domain to which the VLAN IP range belongs", since = "4.19.2.0") private String domainPath; @SerializedName(ApiConstants.POD_ID) - @Param(description = "the Pod ID for the VLAN IP range") + @Param(description = "The Pod ID for the VLAN IP range") private String podId; @SerializedName("podname") - @Param(description = "the Pod name for the VLAN IP range") + @Param(description = "The Pod name for the VLAN IP range") private String podName; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "the description of the VLAN IP range") + @Param(description = "The description of the VLAN IP range") private String description; @SerializedName(ApiConstants.GATEWAY) - @Param(description = "the gateway of the VLAN IP range") + @Param(description = "The gateway of the VLAN IP range") private String gateway; @SerializedName(ApiConstants.NETMASK) - @Param(description = "the netmask of the VLAN IP range") + @Param(description = "The netmask of the VLAN IP range") private String netmask; @SerializedName(ApiConstants.CIDR) - @Param(description = "the cidr of the VLAN IP range") + @Param(description = "The CIDR of the VLAN IP range") private String cidr; @SerializedName(ApiConstants.START_IP) - @Param(description = "the start ip of the VLAN IP range") + @Param(description = "The start IP of the VLAN IP range") private String startIp; @SerializedName(ApiConstants.END_IP) - @Param(description = "the end ip of the VLAN IP range") + @Param(description = "The end IP of the VLAN IP range") private String endIp; @SerializedName(ApiConstants.NETWORK_ID) - @Param(description = "the network id of vlan range") + @Param(description = "The Network ID of VLAN range") private String networkId; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the vlan range") + @Param(description = "The project ID of the VLAN range") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the vlan range") + @Param(description = "The project name of the VLAN range") private String projectName; @SerializedName(ApiConstants.PHYSICAL_NETWORK_ID) - @Param(description = "the physical network this belongs to") + @Param(description = "The physical Network this belongs to") private String physicalNetworkId; @SerializedName(ApiConstants.START_IPV6) - @Param(description = "the start ipv6 of the VLAN IP range") + @Param(description = "The start IPv6 of the VLAN IP range") private String startIpv6; @SerializedName(ApiConstants.END_IPV6) - @Param(description = "the end ipv6 of the VLAN IP range") + @Param(description = "The end IPv6 of the VLAN IP range") private String endIpv6; @SerializedName(ApiConstants.IP6_GATEWAY) - @Param(description = "the gateway of IPv6 network") + @Param(description = "The gateway of IPv6 Network") private String ip6Gateway; @SerializedName(ApiConstants.IP6_CIDR) - @Param(description = "the cidr of IPv6 network") + @Param(description = "The CIDR of IPv6 Network") private String ip6Cidr; @SerializedName(ApiConstants.FOR_SYSTEM_VMS) - @Param(description = "indicates whether VLAN IP range is dedicated to system vms or not") + @Param(description = "Indicates whether VLAN IP range is dedicated to System VMs or not") private Boolean forSystemVms; @SerializedName(ApiConstants.PROVIDER) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VmwareDatacenterResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VmwareDatacenterResponse.java index 3cf06f382428..3bfe61db5ff4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VmwareDatacenterResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VmwareDatacenterResponse.java @@ -33,7 +33,7 @@ public class VmwareDatacenterResponse extends BaseResponse { private String id; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the Zone ID associated with this VMware Datacenter") + @Param(description = "The Zone ID associated with this VMware Datacenter") private Long zoneId; @SerializedName(ApiConstants.NAME) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VolumeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VolumeResponse.java index 209ca57c50d2..031d9315cd08 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VolumeResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VolumeResponse.java @@ -38,7 +38,7 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "name of the disk volume") + @Param(description = "Name of the disk volume") private String name; @SerializedName(ApiConstants.ZONE_ID) @@ -46,103 +46,103 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "name of the availability zone") + @Param(description = "Name of the availability zone") private String zoneName; @SerializedName(ApiConstants.TYPE) - @Param(description = "type of the disk volume (ROOT or DATADISK)") + @Param(description = "Type of the disk volume (ROOT or DATADISK)") private String volumeType; @SerializedName(ApiConstants.DEVICE_ID) - @Param(description = "the ID of the device on user vm the volume is attahed to. This tag is not returned when the volume is detached.") + @Param(description = "The ID of the device on User Instance the volume is attached to. This tag is not returned when the volume is detached.") private Long deviceId; @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) - @Param(description = "id of the virtual machine") + @Param(description = "ID of the Instance") private String virtualMachineId; @SerializedName("isoid") - @Param(description = "the ID of the ISO attached to the virtual machine") + @Param(description = "The ID of the ISO attached to the Instance") private String isoId; @SerializedName("isoname") - @Param(description = "the name of the ISO attached to the virtual machine") + @Param(description = "The name of the ISO attached to the Instance") private String isoName; @SerializedName("isodisplaytext") - @Param(description = "an alternate display text of the ISO attached to the virtual machine") + @Param(description = "An alternate display text of the ISO attached to the Instance") private String isoDisplayText; @SerializedName(ApiConstants.TEMPLATE_ID) - @Param(description = "the ID of the template for the virtual machine. A -1 is returned if the virtual machine was created from an ISO file.") + @Param(description = "The ID of the Template for the Instance. A -1 is returned if the Instance was created from an ISO file.") private String templateId; @SerializedName("templatename") - @Param(description = "the name of the template for the virtual machine") + @Param(description = "The name of the Template for the Instance") private String templateName; @SerializedName("templatedisplaytext") - @Param(description = " an alternate display text of the template for the virtual machine") + @Param(description = "An alternate display text of the Template for the Instance") private String templateDisplayText; @SerializedName("vmname") - @Param(description = "name of the virtual machine") + @Param(description = "Name of the Instance") private String virtualMachineName; @SerializedName("vmdisplayname") - @Param(description = "display name of the virtual machine") + @Param(description = "Display name of the Instance") private String virtualMachineDisplayName; @SerializedName(ApiConstants.VIRTUAL_MACHINE_STATE) - @Param(description = "state of the virtual machine") + @Param(description = "State of the Instance") private String virtualMachineState; @SerializedName(ApiConstants.VM_TYPE) - @Param(description = "type of the virtual machine") + @Param(description = "Type of the Instance") private String vmType; @SerializedName(ApiConstants.PROVISIONINGTYPE) - @Param(description = "provisioning type used to create volumes.") + @Param(description = "Provisioning type used to create volumes.") private String provisioningType; @SerializedName(ApiConstants.SIZE) - @Param(description = "size of the disk volume") + @Param(description = "Size of the disk volume") private Long size; @SerializedName(ApiConstants.MIN_IOPS) - @Param(description = "min iops of the disk volume") + @Param(description = "Min IOPS of the disk volume") private Long minIops; @SerializedName(ApiConstants.MAX_IOPS) - @Param(description = "max iops of the disk volume") + @Param(description = "Max IOPS of the disk volume") private Long maxIops; @SerializedName(ApiConstants.CREATED) - @Param(description = "the date the disk volume was created") + @Param(description = "The date the disk volume was created") private Date created; @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the disk volume") + @Param(description = "The state of the disk volume") private String state; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account associated with the disk volume") + @Param(description = "The Account associated with the disk volume") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the vpn") + @Param(description = "The project id of the VPN") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the vpn") + @Param(description = "The project name of the VPN") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the ID of the domain associated with the disk volume") + @Param(description = "The ID of the domain associated with the disk volume") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain associated with the disk volume") + @Param(description = "The domain associated with the disk volume") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) @@ -150,15 +150,15 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co private String domainPath; @SerializedName("storagetype") - @Param(description = "shared or local storage") + @Param(description = "Shared or local storage") private String storageType; @SerializedName("diskBytesReadRate") - @Param(description = "bytes read rate of the disk volume") + @Param(description = "Bytes read rate of the disk volume") private Long bytesReadRate; @SerializedName("diskBytesWriteRate") - @Param(description = "bytes write rate of the disk volume") + @Param(description = "Bytes write rate of the disk volume") private Long bytesWriteRate; @SerializedName("diskIopsReadRate") @@ -170,19 +170,19 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co private Long iopsWriteRate; @SerializedName(ApiConstants.DISK_KBS_READ) - @Param(description = "the VM's disk read in KiB") + @Param(description = "The Instance's disk read in KiB") private Long diskKbsRead; @SerializedName(ApiConstants.DISK_KBS_WRITE) - @Param(description = "the VM's disk write in KiB") + @Param(description = "The Instance's disk write in KiB") private Long diskKbsWrite; @SerializedName(ApiConstants.DISK_IO_READ) - @Param(description = "the read (IO) of disk on the vm") + @Param(description = "The read (IO) of disk on the Instance") private Long diskIORead; @SerializedName(ApiConstants.DISK_IO_WRITE) - @Param(description = "the write (IO) of disk on the vm") + @Param(description = "The write (IO) of disk on the Instance") private Long diskIOWrite; @SerializedName(ApiConstants.HYPERVISOR) @@ -194,27 +194,27 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co private String diskOfferingId; @SerializedName("diskofferingname") - @Param(description = "name of the disk offering") + @Param(description = "Name of the disk offering") private String diskOfferingName; @SerializedName("diskofferingdisplaytext") - @Param(description = "the display text of the disk offering") + @Param(description = "The display text of the disk offering") private String diskOfferingDisplayText; @SerializedName("storage") - @Param(description = "name of the primary storage hosting the disk volume") + @Param(description = "Name of the primary storage hosting the disk volume") private String storagePoolName; @SerializedName(ApiConstants.SNAPSHOT_ID) - @Param(description = "ID of the snapshot from which this volume was created") + @Param(description = "ID of the Snapshot from which this volume was created") private String snapshotId; @SerializedName("attached") - @Param(description = "the date the volume was attached to a VM instance") + @Param(description = "The date the volume was attached to an Instance") private Date attached; @SerializedName("destroyed") - @Param(description = "the boolean state of whether the volume is destroyed or not") + @Param(description = "The boolean state of whether the volume is destroyed or not") private boolean destroyed; @SerializedName(ApiConstants.SERVICE_OFFERING_ID) @@ -222,43 +222,43 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co private String serviceOfferingId; @SerializedName("serviceofferingname") - @Param(description = "name of the service offering for root disk") + @Param(description = "Name of the service offering for root disk") private String serviceOfferingName; @SerializedName("serviceofferingdisplaytext") - @Param(description = "the display text of the service offering for root disk") + @Param(description = "The display text of the service offering for root disk") private String serviceOfferingDisplayText; @SerializedName("isextractable") - @Param(description = "true if the volume is extractable, false otherwise") + @Param(description = "True if the volume is extractable, false otherwise") private boolean extractable; @SerializedName(ApiConstants.STATUS) - @Param(description = "the status of the volume") + @Param(description = "The status of the volume") private String status; @SerializedName(ApiConstants.DISPLAY_VOLUME) - @Param(description = "an optional field whether to the display the volume to the end user or not.", authorized = {RoleType.Admin}) + @Param(description = "An optional field whether to the display the volume to the end User or not.", authorized = {RoleType.Admin}) private boolean displayVolume; @SerializedName(ApiConstants.PATH) - @Param(description = "the path of the volume") + @Param(description = "The path of the volume") private String path; @SerializedName(ApiConstants.STORAGE_ID) - @Param(description = "id of the primary storage hosting the disk volume; returned to admin user only", since = "4.3") + @Param(description = "ID of the primary storage hosting the disk volume; returned to admin User only", since = "4.3") private String storagePoolId; @SerializedName(ApiConstants.CHAIN_INFO) - @Param(description = "the chain info of the volume", since = "4.4") + @Param(description = "The chain info of the volume", since = "4.4") String chainInfo; @SerializedName(ApiConstants.SNAPSHOT_QUIESCEVM) - @Param(description = "need quiesce vm or not when taking snapshot", since = "4.3") + @Param(description = "Need quiesce Instance or not when taking Snapshot", since = "4.3") private boolean needQuiescevm; @SerializedName(ApiConstants.SUPPORTS_STORAGE_SNAPSHOT) - @Param(description = "true if storage snapshot is supported for the volume, false otherwise", since = "4.16") + @Param(description = "True if storage Snapshot is supported for the volume, false otherwise", since = "4.16") private boolean supportsStorageSnapshot; @SerializedName(ApiConstants.DELETE_PROTECTION) @@ -266,35 +266,35 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co private boolean deleteProtection; @SerializedName(ApiConstants.PHYSICAL_SIZE) - @Param(description = "the bytes actually consumed on disk") + @Param(description = "The bytes actually consumed on disk") private Long physicalsize; @SerializedName(ApiConstants.VIRTUAL_SIZE) - @Param(description = "the bytes allocated") + @Param(description = "The bytes allocated") private Long virtualsize; @SerializedName(ApiConstants.UTILIZATION) - @Param(description = "the disk utilization") + @Param(description = "The disk utilization") private String utilization; @SerializedName(ApiConstants.CLUSTER_ID) - @Param(description = "cluster id of the volume") + @Param(description = "Cluster id of the volume") private String clusterId; @SerializedName(ApiConstants.CLUSTER_NAME) - @Param(description = "cluster name where the volume is allocated") + @Param(description = "Cluster name where the volume is allocated") private String clusterName; @SerializedName(ApiConstants.POD_ID) - @Param(description = "pod id of the volume") + @Param(description = "Pod id of the volume") private String podId; @SerializedName(ApiConstants.POD_NAME) - @Param(description = "pod name of the volume") + @Param(description = "Pod name of the volume") private String podName; @SerializedName(ApiConstants.EXTERNAL_UUID) - @Param(description = "volume uuid that is given by virtualisation provider (only for VMware)") + @Param(description = "Volume UUID that is given by virtualisation provider (only for VMware)") private String externalUuid; @SerializedName(ApiConstants.VOLUME_CHECK_RESULT) @@ -309,6 +309,18 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co @Param(description = "the format of the disk encryption if applicable", since = "4.19.1") private String encryptionFormat; + @SerializedName(ApiConstants.KMS_KEY) + @Param(description = "KMS key name of the volume", since = "4.23.0") + private String kmsKey; + + @SerializedName(ApiConstants.KMS_KEY_ID) + @Param(description = "KMS key id of the volume", since = "4.23.0") + private String kmsKeyId; + + @SerializedName(ApiConstants.KMS_KEY_VERSION) + @Param(description = "Version number of the KMS key used for disk encryption if applicable", since = "4.23.0") + private Integer kmsKeyVersion; + public String getPath() { return path; } @@ -868,7 +880,35 @@ public void setVolumeRepairResult(Map volumeRepairResult) { this.volumeRepairResult = volumeRepairResult; } + public String getEncryptionFormat() { + return encryptionFormat; + } + public void setEncryptionFormat(String encryptionFormat) { this.encryptionFormat = encryptionFormat; } + + public String getKmsKey() { + return kmsKey; + } + + public void setKmsKey(String kmsKey) { + this.kmsKey = kmsKey; + } + + public String getKmsKeyId() { + return kmsKeyId; + } + + public void setKmsKeyId(String kmsKeyId) { + this.kmsKeyId = kmsKeyId; + } + + public Integer getKmsKeyVersion() { + return kmsKeyVersion; + } + + public void setKmsKeyVersion(Integer kmsKeyVersion) { + this.kmsKeyVersion = kmsKeyVersion; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VpcOfferingResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VpcOfferingResponse.java index b11764da7d98..2e821dae52de 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VpcOfferingResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VpcOfferingResponse.java @@ -31,43 +31,43 @@ @SuppressWarnings("unused") public class VpcOfferingResponse extends BaseResponse { @SerializedName("id") - @Param(description = "the id of the vpc offering") + @Param(description = "The ID of the VPC offering") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the vpc offering") + @Param(description = "The name of the VPC offering") private String name; @SerializedName(ApiConstants.DISPLAY_TEXT) - @Param(description = "an alternate display text of the vpc offering.") + @Param(description = "An alternate display text of the VPC offering.") private String displayText; @SerializedName(ApiConstants.CREATED) - @Param(description = "the date this vpc offering was created") + @Param(description = "The date this VPC offering was created") private Date created; @SerializedName(ApiConstants.IS_DEFAULT) - @Param(description = "true if vpc offering is default, false otherwise") + @Param(description = "True if VPC offering is default, false otherwise") private Boolean isDefault; @SerializedName(ApiConstants.STATE) - @Param(description = "state of the vpc offering. Can be Disabled/Enabled") + @Param(description = "State of the VPC offering. Can be Disabled/Enabled") private String state; @SerializedName(ApiConstants.SERVICE) - @Param(description = "the list of supported services", responseObject = ServiceResponse.class) + @Param(description = "The list of supported services", responseObject = ServiceResponse.class) private List services; @SerializedName(ApiConstants.DISTRIBUTED_VPC_ROUTER) - @Param(description = " indicates if the vpc offering supports distributed router for one-hop forwarding", since = "4.4") + @Param(description = "Indicates if the VPC offering supports distributed router for one-hop forwarding", since = "4.4") private Boolean supportsDistributedRouter; @SerializedName((ApiConstants.SUPPORTS_REGION_LEVEL_VPC)) - @Param(description = "indicated if the offering can support region level vpc", since = "4.4") + @Param(description = "Indicated if the offering can support region level VPC", since = "4.4") private Boolean supportsRegionLevelVpc; @SerializedName(ApiConstants.FOR_NSX) - @Param(description = "true if vpc offering can be used by NSX networks only") + @Param(description = "True if VPC offering can be used by NSX networks only") private Boolean forNsx; @SerializedName(ApiConstants.NETWORK_MODE) @@ -75,33 +75,37 @@ public class VpcOfferingResponse extends BaseResponse { private String networkMode; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain ID(s) this disk offering belongs to. Ignore this information as it is not currently applicable.") + @Param(description = "The domain ID(s) this disk offering belongs to. Ignore this information as it is not currently applicable.") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name(s) this disk offering belongs to. Ignore this information as it is not currently applicable.") + @Param(description = "The domain name(s) this disk offering belongs to. Ignore this information as it is not currently applicable.") private String domain; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the zone ID(s) this disk offering belongs to. Ignore this information as it is not currently applicable.", since = "4.13.0") + @Param(description = "The zone ID(s) this disk offering belongs to. Ignore this information as it is not currently applicable.", since = "4.13.0") private String zoneId; @SerializedName(ApiConstants.ZONE) - @Param(description = "the zone name(s) this disk offering belongs to. Ignore this information as it is not currently applicable.", since = "4.13.0") + @Param(description = "The zone name(s) this disk offering belongs to. Ignore this information as it is not currently applicable.", since = "4.13.0") private String zone; @SerializedName(ApiConstants.INTERNET_PROTOCOL) - @Param(description = "the internet protocol of the vpc offering") + @Param(description = "The internet protocol of the VPC offering") private String internetProtocol; @SerializedName(ApiConstants.SPECIFY_AS_NUMBER) - @Param(description = "true if network offering supports choosing AS numbers") + @Param(description = "True if network offering supports choosing AS numbers") private Boolean specifyAsNumber; @SerializedName(ApiConstants.ROUTING_MODE) - @Param(description = "the routing mode for the network offering, supported types are Static or Dynamic.") + @Param(description = "The routing mode for the network offering, supported types are Static or Dynamic.") private String routingMode; + @SerializedName(ApiConstants.CONSERVE_MODE) + @Param(description = "True if the VPC offering is IP conserve mode enabled, allowing public IP services to be used across multiple VPC tiers.", since = "4.23.0") + private Boolean conserveMode; + public void setId(String id) { this.id = id; } @@ -201,4 +205,12 @@ public String getRoutingMode() { public void setRoutingMode(String routingMode) { this.routingMode = routingMode; } + + public Boolean getConserveMode() { + return conserveMode; + } + + public void setConserveMode(Boolean conserveMode) { + this.conserveMode = conserveMode; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java index 56479506686a..34d50d5b9f92 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java @@ -34,103 +34,107 @@ @SuppressWarnings("unused") public class VpcResponse extends BaseResponseWithAnnotations implements ControlledEntityResponse, SetResourceIconResponse { @SerializedName("id") - @Param(description = "the id of the VPC") + @Param(description = "The ID of the VPC") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the VPC") + @Param(description = "The name of the VPC") private String name; @SerializedName(ApiConstants.DISPLAY_TEXT) - @Param(description = "an alternate display text of the VPC.") + @Param(description = "An alternate display text of the VPC.") private String displayText; @SerializedName(ApiConstants.STATE) - @Param(description = "state of the VPC. Can be Inactive/Enabled") + @Param(description = "State of the VPC. Can be Inactive/Enabled") private String state; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "zone id of the vpc") + @Param(description = "Zone ID of the VPC") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "the name of the zone the VPC belongs to") + @Param(description = "The name of the zone the VPC belongs to") private String zoneName; @SerializedName(ApiConstants.SERVICE) - @Param(description = "the list of supported services", responseObject = ServiceResponse.class) + @Param(description = "The list of supported services", responseObject = ServiceResponse.class) private List services; @SerializedName(ApiConstants.CIDR) - @Param(description = "the cidr the VPC") + @Param(description = "The CIDR the VPC") private String cidr; @SerializedName(ApiConstants.VPC_OFF_ID) - @Param(description = "vpc offering id the VPC is created from") + @Param(description = "VPC offering ID the VPC is created from") private String vpcOfferingId; @SerializedName(ApiConstants.VPC_OFF_NAME) - @Param(description = "vpc offering name the VPC is created from", since = "4.13.2") + @Param(description = "VPC offering name the VPC is created from", since = "4.13.2") private String vpcOfferingName; + @SerializedName(ApiConstants.VPC_OFFERING_CONSERVE_MODE) + @Param(description = "true if VPC offering is ip conserve mode enabled", since = "4.23") + private Boolean vpcOfferingConserveMode; + @SerializedName(ApiConstants.CREATED) - @Param(description = "the date this VPC was created") + @Param(description = "The date this VPC was created") private Date created; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the owner of the VPC") + @Param(description = "The owner of the VPC") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the VPC") + @Param(description = "The project ID of the VPC") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the VPC") + @Param(description = "The project name of the VPC") private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain id of the VPC owner") + @Param(description = "The domain ID of the VPC owner") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name of the owner") + @Param(description = "The domain name of the owner") private String domain; @SerializedName(ApiConstants.DOMAIN_PATH) - @Param(description = "the domain path of the owner", since = "4.19.2.0") + @Param(description = "The domain path of the owner", since = "4.19.2.0") private String domainPath; @SerializedName(ApiConstants.NETWORK) - @Param(description = "the list of networks belongign to the VPC", responseObject = NetworkResponse.class) + @Param(description = "The list of Networks belonging to the VPC", responseObject = NetworkResponse.class) private List networks; @SerializedName(ApiConstants.RESTART_REQUIRED) - @Param(description = "true VPC requires restart") + @Param(description = "True VPC requires restart") private Boolean restartRequired; @SerializedName(ApiConstants.NETWORK_DOMAIN) - @Param(description = "the network domain of the VPC") + @Param(description = "The Network domain of the VPC") private String networkDomain; @SerializedName(ApiConstants.TAGS) - @Param(description = "the list of resource tags associated with the project", responseObject = ResourceTagResponse.class) + @Param(description = "The list of resource tags associated with the project", responseObject = ResourceTagResponse.class) private List tags; @SerializedName(ApiConstants.FOR_DISPLAY) - @Param(description = "is vpc for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) + @Param(description = "Is VPC for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; @SerializedName(ApiConstants.DISTRIBUTED_VPC_ROUTER) - @Param(description = "is VPC uses distributed router for one hop forwarding and host based network ACL's", since = "4.4") + @Param(description = "Does VPC use distributed router for one hop forwarding and host based Network ACL's", since = "4.4") private boolean usesDistributedRouter; @SerializedName(ApiConstants.REGION_LEVEL_VPC) - @Param(description = "true if VPC is region level", since = "4.4") + @Param(description = "True if VPC is region level", since = "4.4") private Boolean regionLevelVpc; @SerializedName(ApiConstants.REDUNDANT_VPC_ROUTER) - @Param(description = "if this VPC has redundant router", since = "4.6") + @Param(description = "If this VPC has redundant router", since = "4.6") private boolean redundantRouter; @SerializedName(ApiConstants.RESOURCE_ICON) @@ -138,7 +142,7 @@ public class VpcResponse extends BaseResponseWithAnnotations implements Controll ResourceIconResponse icon; @SerializedName(ApiConstants.IPV6_ROUTES) - @Param(description = "The routes for the network to ease adding route in upstream router", since = "4.17.0") + @Param(description = "The routes for the Network to ease adding route in upstream router", since = "4.17.0") private Set ipv6Routes; @SerializedName(ApiConstants.PUBLIC_MTU) @@ -146,19 +150,19 @@ public class VpcResponse extends BaseResponseWithAnnotations implements Controll private Integer publicMtu; @SerializedName(ApiConstants.DNS1) - @Param(description = "the first IPv4 DNS for the VPC") + @Param(description = "The first IPv4 DNS for the VPC") private String dns1; @SerializedName(ApiConstants.DNS2) - @Param(description = "the second IPv4 DNS for the VPC") + @Param(description = "The second IPv4 DNS for the VPC") private String dns2; @SerializedName(ApiConstants.IP6_DNS1) - @Param(description = "the first IPv6 DNS for the VPC", since = "4.18.0") + @Param(description = "The first IPv6 DNS for the VPC", since = "4.18.0") private String ipv6Dns1; @SerializedName(ApiConstants.IP6_DNS2) - @Param(description = "the second IPv6 DNS for the VPC", since = "4.18.0") + @Param(description = "The second IPv6 DNS for the VPC", since = "4.18.0") private String ipv6Dns2; @SerializedName(ApiConstants.IPV4_ROUTING) @@ -181,6 +185,10 @@ public class VpcResponse extends BaseResponseWithAnnotations implements Controll @Param(description = "The BGP peers for the VPC", since = "4.20.0") private Set bgpPeers; + @SerializedName(ApiConstants.KEEP_MAC_ADDRESS_ON_PUBLIC_NIC) + @Param(description = ApiConstants.PARAMETER_DESCRIPTION_KEEP_MAC_ADDRESS_ON_PUBLIC_NIC, since = "4.23.0") + private Boolean keepMacAddressOnPublicNic; + public void setId(final String id) { this.id = id; } @@ -197,6 +205,10 @@ public void setDisplayText(final String displayText) { this.displayText = displayText; } + public void setVpcOfferingConserveMode(Boolean vpcOfferingConserveMode) { + this.vpcOfferingConserveMode = vpcOfferingConserveMode; + } + public void setCreated(final Date created) { this.created = created; } @@ -358,4 +370,8 @@ public void addBgpPeer(BgpPeerResponse bgpPeer) { } this.bgpPeers.add(bgpPeer); } + + public void setKeepMacAddressOnPublicNic(Boolean keepMacAddressOnPublicNic) { + this.keepMacAddressOnPublicNic = keepMacAddressOnPublicNic; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VpnUsersResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VpnUsersResponse.java index 3247f534133c..cdf4dbe32b81 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VpnUsersResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VpnUsersResponse.java @@ -29,39 +29,39 @@ @SuppressWarnings("unused") public class VpnUsersResponse extends BaseResponse implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the vpn userID") + @Param(description = "The VPN user ID") private String id; @SerializedName(ApiConstants.USERNAME) - @Param(description = "the username of the vpn user") + @Param(description = "The username of the VPN user") private String userName; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "the account of the remote access vpn") + @Param(description = "The Account of the remote access VPN") private String accountName; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the domain id of the account of the remote access vpn") + @Param(description = "The domain ID of the Account of the remote access VPN") private String domainId; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "the domain name of the account of the remote access vpn") + @Param(description = "The domain name of the Account of the remote access VPN") private String domainName; @SerializedName(ApiConstants.DOMAIN_PATH) - @Param(description = "path of the domain to which the remote access vpn belongs", since = "4.19.2.0") + @Param(description = "Path of the domain to which the remote access VPN belongs", since = "4.19.2.0") private String domainPath; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the vpn") + @Param(description = "The project ID of the VPN") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the vpn") + @Param(description = "The project name of the VPN") private String projectName; @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the Vpn User, can be 'Add', 'Revoke' or 'Active'.") + @Param(description = "The state of the VPN User, can be 'Add', 'Revoke' or 'Active'.") private String state; public void setId(String id) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VsphereStoragePoliciesResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VsphereStoragePoliciesResponse.java index 63c49f148790..3d1518a1cae3 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VsphereStoragePoliciesResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VsphereStoragePoliciesResponse.java @@ -28,23 +28,23 @@ public class VsphereStoragePoliciesResponse extends BaseResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the Storage Policy") + @Param(description = "The ID of the Storage Policy") private String id; @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the ID of the Zone") + @Param(description = "The ID of the Zone") private String zoneId; @SerializedName(ApiConstants.POLICY_ID) - @Param(description = "the identifier of the Storage Policy in vSphere DataCenter") + @Param(description = "The identifier of the Storage Policy in vSphere DataCenter") private String policyId; @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the Storage Policy") + @Param(description = "The name of the Storage Policy") private String name; @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "the description of the Storage Policy") + @Param(description = "The description of the Storage Policy") private String description; public String getId() { 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 09e53dbb1462..a05ee925ac09 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 @@ -46,55 +46,55 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso private String description; @SerializedName(ApiConstants.DNS1) - @Param(description = "the first DNS for the Zone") + @Param(description = "The first DNS for the Zone") private String dns1; @SerializedName(ApiConstants.DNS2) - @Param(description = "the second DNS for the Zone") + @Param(description = "The second DNS for the Zone") private String dns2; @SerializedName(ApiConstants.IP6_DNS1) - @Param(description = "the first IPv6 DNS for the Zone") + @Param(description = "The first IPv6 DNS for the Zone") private String ip6Dns1; @SerializedName(ApiConstants.IP6_DNS2) - @Param(description = "the second IPv6 DNS for the Zone") + @Param(description = "The second IPv6 DNS for the Zone") private String ip6Dns2; @SerializedName(ApiConstants.INTERNAL_DNS1) - @Param(description = "the first internal DNS for the Zone") + @Param(description = "The first internal DNS for the Zone") private String internalDns1; @SerializedName(ApiConstants.INTERNAL_DNS2) - @Param(description = "the second internal DNS for the Zone") + @Param(description = "The second internal DNS for the Zone") private String internalDns2; @SerializedName(ApiConstants.GUEST_CIDR_ADDRESS) - @Param(description = "the guest CIDR address for the Zone") + @Param(description = "The guest CIDR address for the Zone") private String guestCidrAddress; @SerializedName(ApiConstants.DISPLAY_TEXT) - @Param(description = "the display text of the zone") + @Param(description = "The display text of the zone") private String displayText; @SerializedName(ApiConstants.DOMAIN) - @Param(description = "Network domain name for the networks in the zone") + @Param(description = "Network domain name for the Networks in the zone") private String domain; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "the UUID of the containing domain, null for public zones") + @Param(description = "The UUID of the containing domain, null for public zones") private String domainId; @SerializedName("domainname") - @Param(description = "the name of the containing domain, null for public zones") + @Param(description = "The name of the containing domain, null for public zones") private String domainName; @SerializedName(ApiConstants.NETWORK_TYPE) - @Param(description = "the network type of the zone; can be Basic or Advanced") + @Param(description = "The Network type of the zone; can be Basic or Advanced") private String networkType; @SerializedName("securitygroupsenabled") - @Param(description = "true if security groups support is enabled, false otherwise") + @Param(description = "True if security groups support is enabled, false otherwise") private Boolean securityGroupsEnabled; @SerializedName("gputotal") @@ -106,7 +106,7 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso private Long gpuUsed; @SerializedName("allocationstate") - @Param(description = "the allocation state of the cluster") + @Param(description = "The allocation state of the cluster") private String allocationState; @SerializedName(ApiConstants.ZONE_TOKEN) @@ -114,19 +114,19 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso private String zoneToken; @SerializedName(ApiConstants.DHCP_PROVIDER) - @Param(description = "the dhcp Provider for the Zone") + @Param(description = "The DHCP Provider for the Zone") private String dhcpProvider; @SerializedName("capacity") - @Param(description = "the capacity of the Zone", responseObject = CapacityResponse.class) + @Param(description = "The capacity of the Zone", responseObject = CapacityResponse.class) private List capacities; @SerializedName(ApiConstants.LOCAL_STORAGE_ENABLED) - @Param(description = "true if local storage offering enabled, false otherwise") + @Param(description = "True if local storage offering enabled, false otherwise") private Boolean localStorageEnabled; @SerializedName(ApiConstants.TAGS) - @Param(description = "the list of resource tags associated with zone.", responseObject = ResourceTagResponse.class, since = "4.3") + @Param(description = "The list of resource tags associated with zone.", responseObject = ResourceTagResponse.class, since = "4.3") private Set tags; @SerializedName(ApiConstants.RESOURCE_DETAILS) @@ -150,12 +150,12 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso private Integer routerPublicInterfaceMaxMtu; @SerializedName(ApiConstants.TYPE) - @Param(description = "the type of the zone - core or edge", since = "4.18.0") + @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") + @Param(description = "True, if zone is NSX enabled", since = "4.20.0") private boolean nsxEnabled = false; @SerializedName(ApiConstants.PROVIDER) @@ -163,7 +163,7 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso private String provider = null; @SerializedName(ApiConstants.MULTI_ARCH) - @Param(description = "true, if zone contains clusters and hosts from different CPU architectures", since = "4.20") + @Param(description = "True, if zone contains clusters and hosts from different CPU architectures", since = "4.20") private boolean multiArch; @SerializedName(ApiConstants.ASN_RANGE) @@ -171,7 +171,7 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso private String asnRange; @SerializedName(ApiConstants.ROUTED_MODE_ENABLED) - @Param(description = "true, if routed network/vpc is enabled", since = "4.20.1") + @Param(description = "True, if routed Network/VPC is enabled", since = "4.20.1") private boolean routedModeEnabled = false; @SerializedName(ApiConstants.STORAGE_ACCESS_GROUPS) diff --git a/api/src/main/java/org/apache/cloudstack/auth/UserOAuth2Authenticator.java b/api/src/main/java/org/apache/cloudstack/auth/UserOAuth2Authenticator.java index ee3b98b8a4b6..cccc1fff9823 100644 --- a/api/src/main/java/org/apache/cloudstack/auth/UserOAuth2Authenticator.java +++ b/api/src/main/java/org/apache/cloudstack/auth/UserOAuth2Authenticator.java @@ -42,8 +42,19 @@ public interface UserOAuth2Authenticator extends Adapter { * Verifies the code provided by provider and fetches email * @return returns email */ - String verifyCodeAndFetchEmail(String secretCode); + String verifySecretCodeAndFetchEmail(String secretCode); + /** + * Verifies if the logged in user is valid for a specific domain + * @return true if it's a valid user, otherwise false + */ + boolean verifyUser(String email, String secretCode, Long domainId); + + /** + * Verifies the secret code provided by provider and fetches email for a specific domain + * @return email for the specified domain + */ + String verifySecretCodeAndFetchEmail(String secretCode, Long domainId); /** * Fetches email using the accessToken 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 951af9180e7f..2124423da108 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/Backup.java +++ b/api/src/main/java/org/apache/cloudstack/backup/Backup.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.backup; +import java.io.Serializable; import java.util.Date; import java.util.List; import java.util.Map; @@ -30,8 +31,36 @@ public interface Backup extends ControlledEntity, InternalIdentity, Identity { + String getFromCheckpointId(); + + String getToCheckpointId(); + + Long getCheckpointCreateTime(); + + Long getHostId(); + enum Status { - Allocated, Queued, BackingUp, BackedUp, Error, Failed, Restoring, Removed, Expunged + Allocated, Queued, BackingUp, ReadyForImageTransfer, FinalizingImageTransfer, BackedUp, Error, Failed, Restoring, Removed, Expunged, + // Hidden: a chain backup kept as a tombstone after the user deleted it while it still has + // live descendants (incremental chains). Excluded from listBackups and from all backup + // operations (which require BackedUp); swept from the DB once its last descendant is gone. + Hidden + } + + enum CompressionStatus { + Uncompressed, Compressing, FinalizingCompression, Compressed, CompressionError + } + + enum ValidationStatus { + NotValidated, Validating, Valid, UnableToValidate, NotValid + } + + enum ValidationSteps { + wait_for_boot, screenshot, execute_command + } + + enum CompressionLibrary { + zstd, zlib } class Metric { @@ -120,7 +149,7 @@ public void setDataSize(Long dataSize) { } } - class VolumeInfo { + class VolumeInfo implements Serializable { private String uuid; private Volume.Type type; private Long size; @@ -189,11 +218,14 @@ public String toString() { String getType(); Date getDate(); Backup.Status getStatus(); + Backup.CompressionStatus getCompressionStatus(); + Backup.ValidationStatus getValidationStatus(); Long getSize(); Long getProtectedSize(); void setName(String name); String getDescription(); void setDescription(String description); + Long getUncompressedSize(); List getBackedUpVolumes(); long getZoneId(); Map getDetails(); 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 c4b92fc9e05c..30fc2bbec40d 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java @@ -20,26 +20,29 @@ import java.util.List; import java.util.Map; +import com.cloud.storage.Volume; +import com.cloud.vm.VirtualMachine; import com.cloud.capacity.Capacity; import com.cloud.exception.ResourceAllocationException; +import org.apache.cloudstack.api.command.admin.backup.CloneBackupOfferingCmd; 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.ListBackupScheduleCmd; import org.apache.cloudstack.api.command.user.backup.ListBackupsCmd; +import org.apache.cloudstack.api.command.user.backup.CreateBackupOfferingCmd; 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; /** @@ -55,7 +58,8 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer ConfigKey BackupProviderPlugin = new ConfigKey<>("Advanced", String.class, "backup.framework.provider.plugin", "dummy", - "The backup and recovery provider plugin.", true, ConfigKey.Scope.Zone, BackupFrameworkEnabled.key()); + "The backup and recovery provider plugin.", + true, ConfigKey.Scope.Zone, BackupFrameworkEnabled.key()); ConfigKey BackupSyncPollingInterval = new ConfigKey<>("Advanced", Long.class, "backup.framework.sync.interval", @@ -135,6 +139,20 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer */ BackupOffering importBackupOffering(final ImportBackupOfferingCmd cmd); + /** + * Create a new Backup and Recovery policy to CloudStack. Currently only supported for KBOSS. + * @param cmd create backup offering cmd + */ + BackupOffering createBackupOffering(final CreateBackupOfferingCmd cmd); + + List getBackupOfferingDomains(final Long offeringId); + + /** + * Clone an existing backup offering with updated values + * @param cmd clone backup offering cmd + */ + BackupOffering cloneBackupOffering(final CloneBackupOfferingCmd cmd); + /** * List backup offerings * @param ListBackupOfferingsCmd API cmd @@ -174,7 +192,7 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer * @param vmId * @return */ - List listBackupSchedule(Long vmId); + List listBackupSchedules(ListBackupScheduleCmd cmd); /** * Deletes VM backup schedule for a VM @@ -199,21 +217,23 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer /** * Restore a full VM from backup */ - boolean restoreBackup(final Long backupId); + boolean restoreBackup(final Long backupId, boolean quickRestore, Long hostId); Map getIpToNetworkMapFromBackup(Backup backup, boolean preserveIps, List networkIds); Boolean canCreateInstanceFromBackup(Long backupId); + Boolean canCreateInstanceFromBackupAcrossZones(Long backupId); + /** * Restore a backup to a new Instance */ - boolean restoreBackupToVM(Long backupId, Long vmId) throws ResourceUnavailableException; + boolean restoreBackupToVM(Long backupId, Long vmId, boolean quickrestore) throws ResourceUnavailableException; /** * Restore a backed up volume and attach it to a VM */ - boolean restoreBackupVolumeAndAttachToVM(final String backedUpVolumeUuid, final Long backupId, final Long vmId) throws Exception; + boolean restoreBackupVolumeAndAttachToVM(final String backedUpVolumeUuid, final Long backupId, final Long vmId, boolean isQuickRestore, Long hostId) throws Exception; /** * Deletes a backup @@ -221,7 +241,7 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer * @param forced Indicates if backup will be force removed or not * @return returns operation success */ - boolean deleteBackup(final Long backupId, final Boolean forced); + boolean deleteBackup(final Long backupId, final Boolean forced) throws ResourceAllocationException; void validateBackupForZone(Long zoneId); 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 1eb36f895565..66e4501c460e 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java @@ -23,6 +23,8 @@ public interface BackupProvider { + Boolean crossZoneInstanceCreationEnabled(BackupOffering backupOffering); + /** * Returns the unique name of the provider * @return returns provider name @@ -61,6 +63,7 @@ public interface BackupProvider { */ boolean removeVMFromBackupOffering(VirtualMachine vm); + /** * Whether the provider will delete backups on removal of VM from the offering * @return boolean result @@ -71,11 +74,14 @@ public interface BackupProvider { * Starts and creates an adhoc backup process * for a previously registered VM backup * - * @param vm the machine to make a backup of - * @param quiesceVM instance will be quiesced for checkpointing for backup. Applicable only to NAS plugin. + * @param vm + * the machine to make a backup of + * @param quiesceVM + * instance will be quiesced for checkpointing for backup. Applicable only to NAS plugin. + * @param isolated * @return the result and {code}Backup{code} {code}Object{code} */ - Pair takeBackup(VirtualMachine vm, Boolean quiesceVM); + Pair takeBackup(VirtualMachine vm, Boolean quiesceVM, boolean isolated); /** * Delete an existing backup @@ -85,17 +91,30 @@ public interface BackupProvider { */ boolean deleteBackup(Backup backup, boolean forced); - boolean restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid); + /** + * Whether {@link #deleteBackup(Backup, boolean)} owns DB-row removal and resource-count / + * usage accounting for every backup it physically removes. Providers that manage incremental + * chains (e.g. NAS) delete several backups per call — the leaf plus swept delete-pending + * ancestors — and decrement once per removed backup themselves, so the manager must NOT + * decrement or remove the row again. Defaults to {@code false}: the manager does the + * single-backup accounting (the historical behaviour for non-chain providers). + */ + default boolean handlesChainDeleteResourceAccounting() { + return false; + } + + Pair restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid, boolean quickrestore); /** * Restore VM from backup */ - boolean restoreVMFromBackup(VirtualMachine vm, Backup backup); + boolean restoreVMFromBackup(VirtualMachine vm, Backup backup, boolean quickRestore, Long hostId); /** * Restore a volume from a backup */ - Pair restoreBackedUpVolume(Backup backup, Backup.VolumeInfo backupVolumeInfo, String hostIp, String dataStoreUuid, Pair vmNameAndState); + Pair restoreBackedUpVolume(Backup backup, Backup.VolumeInfo backupVolumeInfo, String hostIp, String dataStoreUuid, + Pair vmNameAndState, VirtualMachine vm, boolean quickRestore); /** * Syncs backup metrics (backup size, protected size) from the plugin and stores it within the provider @@ -122,6 +141,10 @@ public interface BackupProvider { */ boolean supportsInstanceFromBackup(); + default boolean supportsMemoryVmSnapshot() { + return true; + } + /** * Returns the backup storage usage (Used, Total) for a backup provider * @param zoneId the zone for which to return metrics @@ -134,5 +157,4 @@ public interface BackupProvider { * @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 be539a0eb044..886d13c13f93 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupRepository.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupRepository.java @@ -28,9 +28,12 @@ public interface BackupRepository extends InternalIdentity, Identity { String getType(); String getAddress(); String getMountOptions(); + void setMountOptions(String mountOptions); void setUsedBytes(Long usedBytes); Long getCapacityBytes(); Long getUsedBytes(); void setCapacityBytes(Long capacityBytes); + Boolean crossZoneInstanceCreationEnabled(); + void setCrossZoneInstanceCreation(Boolean crossZoneInstanceCreation); Date getCreated(); } diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupRepositoryService.java b/api/src/main/java/org/apache/cloudstack/backup/BackupRepositoryService.java index ae71053e400d..cc8144ebe403 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupRepositoryService.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupRepositoryService.java @@ -23,12 +23,13 @@ import org.apache.cloudstack.api.command.user.backup.repository.AddBackupRepositoryCmd; import org.apache.cloudstack.api.command.user.backup.repository.DeleteBackupRepositoryCmd; import org.apache.cloudstack.api.command.user.backup.repository.ListBackupRepositoriesCmd; +import org.apache.cloudstack.api.command.user.backup.repository.UpdateBackupRepositoryCmd; import java.util.List; public interface BackupRepositoryService { BackupRepository addBackupRepository(AddBackupRepositoryCmd cmd); + BackupRepository updateBackupRepository(UpdateBackupRepositoryCmd cmd); boolean deleteBackupRepository(DeleteBackupRepositoryCmd cmd); Pair, Integer> listBackupRepositories(ListBackupRepositoriesCmd cmd); - } 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 b5138d34de11..ddc823a14ff5 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupSchedule.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupSchedule.java @@ -19,11 +19,12 @@ import java.util.Date; +import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.api.InternalIdentity; import com.cloud.utils.DateUtil; -public interface BackupSchedule extends InternalIdentity { +public interface BackupSchedule extends ControlledEntity, InternalIdentity { Long getVmId(); DateUtil.IntervalType getScheduleType(); String getSchedule(); @@ -33,4 +34,5 @@ public interface BackupSchedule extends InternalIdentity { Boolean getQuiesceVM(); int getMaxBackups(); String getUuid(); + boolean isIsolated(); } diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupService.java b/api/src/main/java/org/apache/cloudstack/backup/BackupService.java index d4beb629fe0f..3ba2978c0fa5 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupService.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupService.java @@ -34,4 +34,11 @@ public interface BackupService { * @return backup provider */ BackupProvider getBackupProvider(final Long zoneId); + + /** + * Find backup provider by name + * @param name backup provider name + * @return backup provider + */ + BackupProvider getBackupProvider(final String name); } diff --git a/server/src/test/java/org/apache/cloudstack/storage/heuristics/presetvariables/DomainTest.java b/api/src/main/java/org/apache/cloudstack/backup/ImageTransfer.java similarity index 52% rename from server/src/test/java/org/apache/cloudstack/storage/heuristics/presetvariables/DomainTest.java rename to api/src/main/java/org/apache/cloudstack/backup/ImageTransfer.java index a1ec6854ecc0..e1153be3ae02 100644 --- a/server/src/test/java/org/apache/cloudstack/storage/heuristics/presetvariables/DomainTest.java +++ b/api/src/main/java/org/apache/cloudstack/backup/ImageTransfer.java @@ -15,27 +15,47 @@ // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.storage.heuristics.presetvariables; +package org.apache.cloudstack.backup; -import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.api.InternalIdentity; -@RunWith(MockitoJUnitRunner.class) -public class DomainTest { +public interface ImageTransfer extends ControlledEntity, InternalIdentity { + long getDataCenterId(); - @Test - public void toStringTestReturnsValidJson() { - Domain variable = new Domain(); - variable.setName("test name"); - variable.setId("test id"); + public enum Direction { + upload, download + } + + public enum Format { + raw, + cow + } - String expected = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(variable, "name", "id"); - String result = variable.toString(); + public enum Backend { + nbd, + file + } - Assert.assertEquals(expected, result); + public enum Phase { + initializing, transferring, finished, failed } + String getUuid(); + + Long getBackupId(); + + long getVolumeId(); + + long getHostId(); + + String getTransferUrl(); + + Phase getPhase(); + + Direction getDirection(); + + Backend getBackend(); + + String getSignedTicketId(); } diff --git a/api/src/main/java/org/apache/cloudstack/backup/InternalBackupProvider.java b/api/src/main/java/org/apache/cloudstack/backup/InternalBackupProvider.java new file mode 100644 index 000000000000..0543fb10af36 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/backup/InternalBackupProvider.java @@ -0,0 +1,142 @@ +// 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.storage.Volume; +import com.cloud.uservm.UserVm; +import com.cloud.utils.Pair; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.snapshot.VMSnapshot; +import org.apache.cloudstack.framework.config.ConfigKey; + +import java.util.Set; + +public interface InternalBackupProvider extends BackupProvider { + String VM_WORK_JOB_HANDLER = InternalBackupService.class.getSimpleName(); + + ConfigKey backupCompressionTimeout = new ConfigKey<>("Advanced", Integer.class, "backup.compression.timeout", "28800", "Backup compression timeout (in " + + "seconds). Will only start counting once the backup compression async job actually starts. This setting is currently only applicable to KBOSS.", true, + ConfigKey.Scope.Cluster); + + ConfigKey backupCompressionMinimumFreeStorage = new ConfigKey<>("Advanced", Double.class, "backup.compression.minimum.free.storage", "1", "The minimum " + + "amount of free storage that should be available to start the compression. This configuration uses a multiplier on the backup size, by default, it needs the same " + + "amount of free storage as the backup uses while uncompressed. This setting is currently only applicable to KBOSS.", true, ConfigKey.Scope.Zone); + + ConfigKey backupCompressionCoroutines = new ConfigKey<>("Advanced", Integer.class, "backup.compression.coroutines", "1", "Number of parallel coroutines " + + "for the compression process. This is translated to qemu-img '-m' parameter. This setting is currently only applicable to KBOSS.", true, ConfigKey.Scope.Cluster); + + ConfigKey backupCompressionRateLimit = new ConfigKey<>("Advanced", Integer.class, "backup.compression.rate.limit", "0", "Limit the compression rate to " + + "this configuration's value (in MB/s). Values lower than 1 disable the limit. This setting is currently only applicable to KBOSS.", true, ConfigKey.Scope.Cluster); + + ConfigKey backupValidationTimeout = new ConfigKey<>("Advanced", Integer.class, "backup.validation.timeout", "3600", "Backup validation job timeout (in " + + "seconds). Will only start counting once the backup validation async job actually starts. This setting is currently only applicable to KBOSS.", true, ConfigKey.Scope.Cluster); + + /** + * Actually execute the backup after being queued. + * */ + default Pair orchestrateTakeBackup(Backup backup, boolean quiesceVm, boolean isolated) { + return null; + } + + /** + * Actually delete the backup after being queued. + * */ + default Boolean orchestrateDeleteBackup(Backup backup, boolean forced) { + return null; + } + + /** + * Actually restore the backup after being queued. + * */ + default Boolean orchestrateRestoreVMFromBackup(Backup backup, VirtualMachine vm, boolean quickRestore, Long hostId, boolean sameVmAsBackup) { + return null; + } + + /** + * This method should be overwritten by any backup providers that want to schedule their backup restore jobs in the same queue as the VM jobs. + * Otherwise, just use the restoreBackedUpVolume method. + * */ + default Pair orchestrateRestoreBackedUpVolume(Backup backup, VirtualMachine vm, Backup.VolumeInfo backupVolumeInfo, String hostIp, boolean quickRestore) { + return null; + } + + /** + * This method should be overwritten by any native backup providers that want to allow backup compression through ACS.
+ * The compression is done in two steps:
+ * 1) Compress the backup to a different file;
+ * 2) Switch the old file for the newly compressed one.
+ *

+ * This method is supposed to execute step 1. + * + * @return + */ + default boolean startBackupCompression(long backupId, long hostId) { + return false; + } + + /** + * This method should be overwritten by any native backup providers that want to allow backup compression through ACS.
+ * The compression is done in two steps:
+ * 1) Compress the backup to a different file;
+ * 2) Switch the old file for the newly compressed one.
+ *

+ * This method is supposed to execute step 2. + * + * @return + */ + default boolean finalizeBackupCompression(long backupId, long hostId) { + return false; + } + + default boolean validateBackup(long backupId, long hostId) { + return false; + } + + /** + * This method should be overwritten by any native backup providers that allow volume detach but need to prepare it beforehand. + * */ + default void prepareVolumeForDetach(Volume volume, VirtualMachine virtualMachine) { + } + + /** + * This method should be overwritten by any native backup providers that allow volume migration but need to prepare it beforehand. + * */ + default void prepareVolumeForMigration(Volume volume, VirtualMachine virtualMachine) { + } + + /** + * This method should be overwritten by any native backup providers that must update metadata regarding a volume after certain operations (such as after a volume migration). + * */ + default void updateVolumeId(VirtualMachine virtualMachine, long oldVolumeId, long newVolumeId) { + } + + default Set getSecondaryStorageUrls(UserVm userVm) { + return Set.of(); + } + + /** + * This method should be overwritten by any native backup providers that are compatible with VM Snapshots but need to prepare the VM to be reverted. + * Currently, the only strategy that calls this method is the {@code KvmFileBasedStorageVmSnapshotStrategy}. + * */ + default void prepareVmForSnapshotRevert(VMSnapshot vmSnapshot, VirtualMachine virtualMachine) { + } + + default boolean finishBackupChain(VirtualMachine virtualMachine) { + return false; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/backup/InternalBackupService.java b/api/src/main/java/org/apache/cloudstack/backup/InternalBackupService.java new file mode 100644 index 000000000000..76c71f0eb4ce --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/backup/InternalBackupService.java @@ -0,0 +1,54 @@ +// 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.to.DataTO; +import com.cloud.storage.Volume; +import com.cloud.uservm.UserVm; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.snapshot.VMSnapshot; +import org.apache.cloudstack.api.response.ExtractResponse; + +import java.util.Set; + +public interface InternalBackupService { + + void configureChainInfo(DataTO volumeTo, Command cmd); + + void cleanupBackupMetadata(long volumeId); + + void prepareVolumeForDetach(Volume volume, VirtualMachine virtualMachine); + + void prepareVolumeForMigration(Volume volume); + + void prepareVmForSnapshotRevert(VMSnapshot vmSnapshot); + + void updateVolumeId(long oldVolumeId, long newVolumeId); + + Set getSecondaryStorageUrls(UserVm userVm); + + boolean startBackupCompression(long backupId, long hostId, long zoneId); + + boolean finalizeBackupCompression(long backupId, long hostId, long zoneId); + + boolean validateBackup(long backupId, long hostId, long zoneId); + + ExtractResponse downloadScreenshot(long backupId); + + boolean finishBackupChain(long vmId); +} diff --git a/api/src/main/java/org/apache/cloudstack/backup/KVMBackupExportService.java b/api/src/main/java/org/apache/cloudstack/backup/KVMBackupExportService.java new file mode 100644 index 000000000000..e1affb311ec6 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/backup/KVMBackupExportService.java @@ -0,0 +1,106 @@ +//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.backup; + +import java.util.List; + +import org.apache.cloudstack.api.command.admin.backup.CreateImageTransferCmd; +import org.apache.cloudstack.api.command.admin.backup.DeleteVmCheckpointCmd; +import org.apache.cloudstack.api.command.admin.backup.FinalizeBackupCmd; +import org.apache.cloudstack.api.command.admin.backup.FinalizeImageTransferCmd; +import org.apache.cloudstack.api.command.admin.backup.ListImageTransfersCmd; +import org.apache.cloudstack.api.command.admin.backup.ListVmCheckpointsCmd; +import org.apache.cloudstack.api.command.admin.backup.StartBackupCmd; +import org.apache.cloudstack.api.response.CheckpointResponse; +import org.apache.cloudstack.api.response.ImageTransferResponse; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; + +import com.cloud.utils.component.PluggableService; + +/** + * Service for Creating Backups and ImageTransfer sessions which will be consumed by an external orchestrator. + */ +public interface KVMBackupExportService extends Configurable, PluggableService { + + ConfigKey ImageTransferIdleTimeoutSeconds = new ConfigKey<>("Advanced", Integer.class, + "image.transfer.idle.timeout.seconds", + "600", + "Seconds since last completed HTTP request to an image transfer before the image server unregisters it (idle timeout).", + true, ConfigKey.Scope.Zone); + + ConfigKey ExposeKVMBackupExportServiceApis = new ConfigKey<>("Advanced", Boolean.class, + "expose.kvm.backup.export.service.apis", + "false", + "Enable to expose APIs for testing the KVM Backup Export Service.", + false, ConfigKey.Scope.Global); + /** + * Creates a backup session for a VM + */ + Backup createBackup(StartBackupCmd cmd); + + /** + * Start a backup session for a VM + * Creates a new checkpoint and starts NBD server for pull-mode backup + */ + Backup startBackup(StartBackupCmd cmd); + + /** + * Finalize a backup session + * Stops NBD server, updates checkpoint tracking, deletes old checkpoints + */ + Backup finalizeBackup(FinalizeBackupCmd cmd); + + /** + * Create an image transfer object for a disk + * Registers NBD endpoint with ImageIO (stubbed for POC) + */ + ImageTransferResponse createImageTransfer(CreateImageTransferCmd cmd); + + ImageTransfer createImageTransfer(long volumeId, Long backupId, ImageTransfer.Direction direction, ImageTransfer.Format format); + + boolean cancelImageTransfer(long imageTransferId); + + /** + * Finalize an image transfer + * Marks transfer as complete (NBD is closed globally in finalize backup) + */ + boolean finalizeImageTransfer(FinalizeImageTransferCmd cmd); + + boolean finalizeImageTransfer(long imageTransferId); + + /** + * List image transfers for a backup + */ + List listImageTransfers(ListImageTransfersCmd cmd); + + /** + * List checkpoints for a VM + */ + List listVmCheckpoints(ListVmCheckpointsCmd cmd); + + /** + * Delete a VM checkpoint (no-op for normal flow, kept for API parity) + */ + boolean deleteVmCheckpoint(DeleteVmCheckpointCmd cmd); + + /** + * List Compatible Data Center Ids for the service + */ + List listCompatibleDataCenterIds(); +} diff --git a/api/src/main/java/org/apache/cloudstack/ca/CAManager.java b/api/src/main/java/org/apache/cloudstack/ca/CAManager.java index b0fb1ac73c21..d2ebdc25f1bd 100644 --- a/api/src/main/java/org/apache/cloudstack/ca/CAManager.java +++ b/api/src/main/java/org/apache/cloudstack/ca/CAManager.java @@ -23,6 +23,8 @@ import java.util.List; import java.util.Map; +import com.trilead.ssh2.Connection; + import org.apache.cloudstack.framework.ca.CAProvider; import org.apache.cloudstack.framework.ca.CAService; import org.apache.cloudstack.framework.ca.Certificate; @@ -39,7 +41,10 @@ public interface CAManager extends CAService, Configurable, PluggableService { ConfigKey CAProviderPlugin = new ConfigKey<>("Advanced", String.class, "ca.framework.provider.plugin", "root", - "The CA provider plugin that is used for secure CloudStack management server-agent communication for encryption and authentication. Restart management server(s) when changed.", true); + "The CA provider plugin used for CloudStack internal certificate management (MS-agent encryption and authentication). " + + "The default 'root' provider auto-generates a CA on first startup, but also supports user-provided custom CA material " + + "via the ca.plugin.root.private.key, ca.plugin.root.public.key, and ca.plugin.root.ca.certificate settings. " + + "Restart management server(s) when changed.", false); ConfigKey CertKeySize = new ConfigKey<>("Advanced", Integer.class, "ca.framework.cert.keysize", @@ -85,6 +90,12 @@ public interface CAManager extends CAService, Configurable, PluggableService { "The actual implementation will depend on the configured CA provider.", false); + ConfigKey CaInjectDefaultTruststore = new ConfigKey<>("Advanced", Boolean.class, + "ca.framework.inject.default.truststore", "true", + "When true, injects the CA provider's certificate into the JVM default truststore on management server startup. " + + "This allows outgoing HTTPS connections from the management server to trust servers with certificates signed by the configured CA. " + + "Restart management server(s) when changed.", false); + /** * Returns a list of available CA provider plugins * @return returns list of CAProvider @@ -130,12 +141,26 @@ public interface CAManager extends CAService, Configurable, PluggableService { boolean revokeCertificate(final BigInteger certSerial, final String certCn, final String provider); /** - * Provisions certificate for given active and connected agent host + * Provisions certificate for given agent host. + * When forced=true, uses SSH to re-provision bypassing the NIO agent connection (for disconnected agents). * @param host + * @param reconnect * @param provider + * @param forced when true, provisions via SSH instead of NIO; supports KVM hosts and SystemVMs * @return returns success/failure as boolean */ - boolean provisionCertificate(final Host host, final Boolean reconnect, final String provider); + boolean provisionCertificate(final Host host, final Boolean reconnect, final String provider, final boolean forced); + + /** + * Provisions certificate for a KVM host using an existing SSH connection. + * Runs keystore-setup to generate a CSR, issues a certificate, then runs keystore-cert-import. + * Used during host discovery and for forced re-provisioning when the NIO agent is unreachable. + * @param sshConnection active SSH connection to the KVM host + * @param agentIp IP address of the KVM host agent + * @param agentHostname hostname of the KVM host agent + * @param caProvider optional CA provider plugin name (null uses default) + */ + void provisionCertificateViaSsh(Connection sshConnection, String agentIp, String agentHostname, String caProvider); /** * Setups up a new keystore and generates CSR for a host diff --git a/api/src/main/java/org/apache/cloudstack/cluster/ClusterDrsAlgorithm.java b/api/src/main/java/org/apache/cloudstack/cluster/ClusterDrsAlgorithm.java index 665f95842b0d..368487c2b9b0 100644 --- a/api/src/main/java/org/apache/cloudstack/cluster/ClusterDrsAlgorithm.java +++ b/api/src/main/java/org/apache/cloudstack/cluster/ClusterDrsAlgorithm.java @@ -22,10 +22,10 @@ import com.cloud.host.Host; import com.cloud.offering.ServiceOffering; import com.cloud.org.Cluster; -import com.cloud.utils.Pair; import com.cloud.utils.Ternary; import com.cloud.utils.component.Adapter; import com.cloud.vm.VirtualMachine; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.math3.stat.descriptive.moment.Mean; import org.apache.commons.math3.stat.descriptive.moment.StandardDeviation; @@ -40,6 +40,9 @@ public interface ClusterDrsAlgorithm extends Adapter { + Mean MEAN_CALCULATOR = new Mean(); + StandardDeviation STDDEV_CALCULATOR = new StandardDeviation(false); + /** * Determines whether a DRS operation is needed for a given cluster and host-VM * mapping. @@ -59,79 +62,121 @@ public interface ClusterDrsAlgorithm extends Adapter { boolean needsDrs(Cluster cluster, List> cpuList, List> memoryList) throws ConfigurationException; - /** - * Determines the metrics for a given virtual machine and destination host in a DRS cluster. - * - * @param clusterId - * the ID of the cluster to check - * @param vm - * the virtual machine to check - * @param serviceOffering - * the service offering for the virtual machine - * @param destHost - * the destination host for the virtual machine - * @param hostCpuMap - * a map of host IDs to the Ternary of used, reserved and total CPU on each host - * @param hostMemoryMap - * a map of host IDs to the Ternary of used, reserved and total memory on each host - * @param requiresStorageMotion - * whether storage motion is required for the virtual machine + * Calculates the metrics (improvement, cost, benefit) for migrating a VM to a destination host. Improvement is + * calculated based on the change in cluster imbalance before and after the migration. * + * @param cluster the cluster to check + * @param vm the virtual machine to check + * @param serviceOffering the service offering for the virtual machine + * @param destHost the destination host for the virtual machine + * @param hostCpuMap a map of host IDs to the Ternary of used, reserved and total CPU on each host + * @param hostMemoryMap a map of host IDs to the Ternary of used, reserved and total memory on each host + * @param requiresStorageMotion whether storage motion is required for the virtual machine + * @param preImbalance the pre-calculated cluster imbalance before migration (null to calculate it) + * @param baseMetricsArray pre-calculated array of all host metrics before migration + * @param hostIdToIndexMap mapping from host ID to index in the metrics array * @return a ternary containing improvement, cost, benefit */ Ternary getMetrics(Cluster cluster, VirtualMachine vm, ServiceOffering serviceOffering, Host destHost, Map> hostCpuMap, Map> hostMemoryMap, - Boolean requiresStorageMotion) throws ConfigurationException; + Boolean requiresStorageMotion, Double preImbalance, + double[] baseMetricsArray, Map hostIdToIndexMap) throws ConfigurationException; /** - * Calculates the imbalance of the cluster after a virtual machine migration. + * Calculates the cluster imbalance after migrating a VM to a destination host. * - * @param serviceOffering - * the service offering for the virtual machine - * @param vm - * the virtual machine being migrated - * @param destHost - * the destination host for the virtual machine - * @param hostCpuMap - * a map of host IDs to the Ternary of used, reserved and total CPU on each host - * @param hostMemoryMap - * a map of host IDs to the Ternary of used, reserved and total memory on each host + * @param vm the virtual machine being migrated + * @param destHost the destination host for the virtual machine + * @param clusterId the cluster ID + * @param vmMetric the VM's resource consumption metric + * @param baseMetricsArray pre-calculated array of all host metrics before migration + * @param hostIdToIndexMap mapping from host ID to index in the metrics array + * @return the cluster imbalance after migration + */ + default Double getImbalancePostMigration(VirtualMachine vm, + Host destHost, Long clusterId, long vmMetric, double[] baseMetricsArray, + Map hostIdToIndexMap, Map> hostCpuMap, + Map> hostMemoryMap) { + // Create a copy of the base array and adjust only the two affected hosts + double[] adjustedMetrics = new double[baseMetricsArray.length]; + System.arraycopy(baseMetricsArray, 0, adjustedMetrics, 0, baseMetricsArray.length); + + long destHostId = destHost.getId(); + long vmHostId = vm.getHostId(); + + // Adjust source host (remove VM resources) + Integer sourceIndex = hostIdToIndexMap.get(vmHostId); + if (sourceIndex != null && sourceIndex < adjustedMetrics.length) { + Map> sourceMetricsMap = getClusterDrsMetric(clusterId).equals("cpu") ? hostCpuMap : hostMemoryMap; + Ternary sourceMetrics = sourceMetricsMap.get(vmHostId); + if (sourceMetrics != null) { + adjustedMetrics[sourceIndex] = getMetricValuePostMigration(clusterId, sourceMetrics, vmMetric, vmHostId, destHostId, vmHostId); + } + } + + // Adjust destination host (add VM resources) + Integer destIndex = hostIdToIndexMap.get(destHostId); + if (destIndex != null && destIndex < adjustedMetrics.length) { + Map> destMetricsMap = getClusterDrsMetric(clusterId).equals("cpu") ? hostCpuMap : hostMemoryMap; + Ternary destMetrics = destMetricsMap.get(destHostId); + if (destMetrics != null) { + adjustedMetrics[destIndex] = getMetricValuePostMigration(clusterId, destMetrics, vmMetric, destHostId, destHostId, vmHostId); + } + } + + return calculateImbalance(adjustedMetrics); + } + + /** + * Calculate imbalance from an array of metric values. + * Imbalance is defined as standard deviation divided by mean. * - * @return a pair containing the CPU and memory imbalance of the cluster after the migration + * Uses reusable stateless calculator objects to avoid object creation overhead. + * @param values array of metric values + * @return calculated imbalance */ - default Double getImbalancePostMigration(ServiceOffering serviceOffering, VirtualMachine vm, - Host destHost, Map> hostCpuMap, - Map> hostMemoryMap) throws ConfigurationException { - Pair>> pair = getHostMetricsMapAndType(destHost.getClusterId(), serviceOffering, hostCpuMap, hostMemoryMap); - long vmMetric = pair.first(); - Map> hostMetricsMap = pair.second(); + private static double calculateImbalance(double[] values) { + if (values == null || values.length == 0) { + return 0.0; + } - List list = new ArrayList<>(); - for (Long hostId : hostMetricsMap.keySet()) { - list.add(getMetricValuePostMigration(destHost.getClusterId(), hostMetricsMap.get(hostId), vmMetric, hostId, destHost.getId(), vm.getHostId())); + double mean = MEAN_CALCULATOR.evaluate(values); + if (mean == 0.0) { + return 0.0; // Avoid division by zero } - return getImbalance(list); + double stdDev = STDDEV_CALCULATOR.evaluate(values, mean); + return stdDev / mean; } - private Pair>> getHostMetricsMapAndType(Long clusterId, - ServiceOffering serviceOffering, Map> hostCpuMap, - Map> hostMemoryMap) throws ConfigurationException { + /** + * Helper method to get VM metric based on cluster configuration. + */ + static long getVmMetric(ServiceOffering serviceOffering, Long clusterId) throws ConfigurationException { String metric = getClusterDrsMetric(clusterId); - Pair>> pair; switch (metric) { case "cpu": - pair = new Pair<>((long) serviceOffering.getCpu() * serviceOffering.getSpeed(), hostCpuMap); - break; + return (long) serviceOffering.getCpu() * serviceOffering.getSpeed(); case "memory": - pair = new Pair<>(serviceOffering.getRamSize() * 1024L * 1024L, hostMemoryMap); - break; + return serviceOffering.getRamSize() * 1024L * 1024L; default: throw new ConfigurationException( String.format("Invalid metric: %s for cluster: %d", metric, clusterId)); } - return pair; + } + + /** + * Helper method to calculate metrics from pre and post imbalance values. + */ + default Ternary calculateMetricsFromImbalances(Double preImbalance, Double postImbalance) { + // This needs more research to determine the cost and benefit of a migration + // TODO: Cost should be a factor of the VM size and the host capacity + // TODO: Benefit should be a factor of the VM size and the host capacity and the number of VMs on the host + final double improvement = preImbalance - postImbalance; + final double cost = 0.0; + final double benefit = 1.0; + return new Ternary<>(improvement, cost, benefit); } private Double getMetricValuePostMigration(Long clusterId, Ternary metrics, long vmMetric, @@ -151,9 +196,26 @@ private Double getMetricValuePostMigration(Long clusterId, Ternary metricList) { - Double clusterMeanMetric = getClusterMeanMetric(metricList); - Double clusterStandardDeviation = getClusterStandardDeviation(metricList, clusterMeanMetric); - return clusterStandardDeviation / clusterMeanMetric; + if (CollectionUtils.isEmpty(metricList)) { + return 0.0; + } + // Convert List to double[] once, avoiding repeated conversions + double[] values = new double[metricList.size()]; + int index = 0; + for (Double value : metricList) { + if (value != null) { + values[index++] = value; + } + } + + // Trim array if some values were null + if (index < values.length) { + double[] trimmed = new double[index]; + System.arraycopy(values, 0, trimmed, 0, index); + values = trimmed; + } + + return calculateImbalance(values); } static String getClusterDrsMetric(long clusterId) { @@ -181,36 +243,6 @@ static Double getMetricValue(long clusterId, long used, long free, long total, F return null; } - /** - * Mean is the average of a collection or set of metrics. In context of a DRS - * cluster, the cluster metrics defined as the average metrics value for some - * metric (such as CPU, memory etc.) for every resource such as host. - * Cluster Mean Metric, mavg = (∑mi) / N, where mi is a measurable metric for a - * resource ‘i’ in a cluster with total N number of resources. - */ - static Double getClusterMeanMetric(List metricList) { - return new Mean().evaluate(metricList.stream().mapToDouble(i -> i).toArray()); - } - - /** - * Standard deviation is defined as the square root of the absolute squared sum - * of difference of a metric from its mean for every resource divided by the - * total number of resources. In context of the DRS, the cluster standard - * deviation is the standard deviation based on a metric of resources in a - * cluster such as for the allocation or utilisation CPU/memory metric of hosts - * in a cluster. - * Cluster Standard Deviation, σc = sqrt((∑∣mi−mavg∣^2) / N), where mavg is the - * mean metric value and mi is a measurable metric for some resource ‘i’ in the - * cluster with total N number of resources. - */ - static Double getClusterStandardDeviation(List metricList, Double mean) { - if (mean != null) { - return new StandardDeviation(false).evaluate(metricList.stream().mapToDouble(i -> i).toArray(), mean); - } else { - return new StandardDeviation(false).evaluate(metricList.stream().mapToDouble(i -> i).toArray()); - } - } - static boolean getDrsMetricUseRatio(long clusterId) { return ClusterDrsMetricUseRatio.valueIn(clusterId); } diff --git a/api/src/main/java/org/apache/cloudstack/config/ApiServiceConfiguration.java b/api/src/main/java/org/apache/cloudstack/config/ApiServiceConfiguration.java index a4aa860487f3..113b97f43c8f 100644 --- a/api/src/main/java/org/apache/cloudstack/config/ApiServiceConfiguration.java +++ b/api/src/main/java/org/apache/cloudstack/config/ApiServiceConfiguration.java @@ -16,10 +16,15 @@ // under the License. package org.apache.cloudstack.config; +import com.cloud.exception.InvalidParameterValueException; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class ApiServiceConfiguration implements Configurable { + protected static Logger LOGGER = LogManager.getLogger(ApiServiceConfiguration.class); public static final ConfigKey ManagementServerAddresses = new ConfigKey<>(String.class, "host", "Advanced", "localhost", "The ip address of management server. This can also accept comma separated addresses.", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.CSV, null); public static final ConfigKey ApiServletPath = new ConfigKey("Advanced", String.class, "endpoint.url", "http://localhost:8080/client/api", "API end point. Can be used by CS components/services deployed remotely, for sending CS API requests", true); @@ -29,6 +34,20 @@ public class ApiServiceConfiguration implements Configurable { "true", "Are the source checks on API calls enabled (true) or not (false)? See api.allowed.source.cidr.list", true, ConfigKey.Scope.Global); public static final ConfigKey ApiAllowedSourceCidrList = new ConfigKey<>(String.class, "api.allowed.source.cidr.list", "Advanced", "0.0.0.0/0,::/0", "Comma separated list of IPv4/IPv6 CIDRs from which API calls can be performed. Can be set on Global and Account levels.", true, ConfigKey.Scope.Account, null, null, null, null, null, ConfigKey.Kind.CSV, null); + + + public static void validateEndpointUrl() { + String csUrl = getApiServletPathValue(); + if (StringUtils.isBlank(csUrl) || StringUtils.containsAny(csUrl, "localhost", "127.0.0.1", "[::1]")) { + LOGGER.error("Global setting [{}] cannot contain localhost or be blank. Current value: {}", ApiServletPath.key(), csUrl); + throw new InvalidParameterValueException("Unable to complete this operation. Contact your cloud admin."); + } + } + + public static String getApiServletPathValue() { + return ApiServletPath.value(); + } + @Override public String getConfigComponentName() { return ApiServiceConfiguration.class.getSimpleName(); diff --git a/api/src/main/java/org/apache/cloudstack/context/CallContext.java b/api/src/main/java/org/apache/cloudstack/context/CallContext.java index 69376e4f6d7d..fcfb5b6b1e00 100644 --- a/api/src/main/java/org/apache/cloudstack/context/CallContext.java +++ b/api/src/main/java/org/apache/cloudstack/context/CallContext.java @@ -63,6 +63,7 @@ protected Stack initialValue() { private User user; private long userId; private final Map context = new HashMap(); + private final Map apiResourcesUuids = new HashMap<>(); private Project project; private String apiName; @@ -180,9 +181,7 @@ protected static CallContext register(User callingUser, Account callingAccount, } s_currentContext.set(callingContext); ThreadContext.push("ctx-" + UuidUtils.first(contextId)); - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Registered: " + callingContext); - } + LOGGER.trace("Registered: {}", callingContext); s_currentContextStack.get().push(callingContext); @@ -279,9 +278,7 @@ public static CallContext unregister() { return null; } s_currentContext.remove(); - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Unregistered: " + context); - } + LOGGER.trace("Unregistered: {}", context); String contextId = context.getContextId(); String sessionIdOnStack = null; String sessionIdPushedToNDC = "ctx-" + UuidUtils.first(contextId); @@ -289,9 +286,7 @@ public static CallContext unregister() { if (sessionIdOnStack.isEmpty() || sessionIdPushedToNDC.equals(sessionIdOnStack)) { break; } - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Popping from NDC: " + contextId); - } + LOGGER.trace("Popping from NDC: {}", contextId); } Stack stack = s_currentContextStack.get(); @@ -394,6 +389,14 @@ public void setEventDisplayEnabled(boolean eventDisplayEnabled) { isEventDisplayEnabled = eventDisplayEnabled; } + public String getApiResourceUuid(String paramName) { + return apiResourcesUuids.get(paramName); + } + + public void putApiResourceUuid(String paramName, String uuid) { + apiResourcesUuids.put(paramName, uuid); + } + public Map getContextParameters() { return context; } diff --git a/api/src/main/java/org/apache/cloudstack/context/LogContext.java b/api/src/main/java/org/apache/cloudstack/context/LogContext.java index c367975aba3b..24b92090e7f8 100644 --- a/api/src/main/java/org/apache/cloudstack/context/LogContext.java +++ b/api/src/main/java/org/apache/cloudstack/context/LogContext.java @@ -136,9 +136,7 @@ protected static LogContext register(User callingUser, Account callingAccount, L } s_currentContext.set(callingContext); ThreadContext.put("logcontextid", UuidUtils.first(contextId)); - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Registered for log: " + callingContext); - } + LOGGER.trace("Registered for log: {}", callingContext); return callingContext; } @@ -207,9 +205,7 @@ public static void unregister() { LogContext context = s_currentContext.get(); if (context != null) { s_currentContext.remove(); - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Unregistered: " + context); - } + LOGGER.trace("Unregistered: {}", context); } ThreadContext.clearMap(); } diff --git a/api/src/main/java/org/apache/cloudstack/dns/DnsProvider.java b/api/src/main/java/org/apache/cloudstack/dns/DnsProvider.java new file mode 100644 index 000000000000..231583244c2a --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/dns/DnsProvider.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.dns; + +import java.util.List; + +import org.apache.cloudstack.dns.exception.DnsProviderException; + +import com.cloud.utils.component.Adapter; + +public interface DnsProvider extends Adapter { + DnsProviderType getProviderType(); + + // Validates connectivity to the server + void validate(DnsServer server) throws Exception; + + String validateAndResolveServer(DnsServer server) throws Exception; + + // Zone Operations + String provisionZone(DnsServer server, DnsZone zone) throws DnsProviderException; + void deleteZone(DnsServer server, DnsZone zone) throws DnsProviderException; + void updateZone(DnsServer server, DnsZone zone) throws DnsProviderException; + + String addRecord(DnsServer server, DnsZone zone, DnsRecord record) throws DnsProviderException; + List listRecords(DnsServer server, DnsZone zone) throws DnsProviderException; + String updateRecord(DnsServer server, DnsZone zone, DnsRecord record) throws DnsProviderException; + String deleteRecord(DnsServer server, DnsZone zone, DnsRecord record) throws DnsProviderException; + boolean dnsRecordExists(DnsServer server, DnsZone zone, String recordName, String recordType) throws DnsProviderException; + boolean dnsZoneExists(DnsServer server, DnsZone zone); +} diff --git a/api/src/main/java/org/apache/cloudstack/dns/DnsProviderManager.java b/api/src/main/java/org/apache/cloudstack/dns/DnsProviderManager.java new file mode 100644 index 000000000000..5430884a32d9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/dns/DnsProviderManager.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.dns; + +import java.util.List; + +import org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd; +import org.apache.cloudstack.api.command.user.dns.AssociateDnsZoneToNetworkCmd; +import org.apache.cloudstack.api.command.user.dns.CreateDnsRecordCmd; +import org.apache.cloudstack.api.command.user.dns.CreateDnsZoneCmd; +import org.apache.cloudstack.api.command.user.dns.DeleteDnsRecordCmd; +import org.apache.cloudstack.api.command.user.dns.DeleteDnsServerCmd; +import org.apache.cloudstack.api.command.user.dns.DisassociateDnsZoneFromNetworkCmd; +import org.apache.cloudstack.api.command.user.dns.ListDnsRecordsCmd; +import org.apache.cloudstack.api.command.user.dns.ListDnsServersCmd; +import org.apache.cloudstack.api.command.user.dns.ListDnsZonesCmd; +import org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd; +import org.apache.cloudstack.api.command.user.dns.UpdateDnsZoneCmd; +import org.apache.cloudstack.api.response.DnsRecordResponse; +import org.apache.cloudstack.api.response.DnsServerResponse; +import org.apache.cloudstack.api.response.DnsZoneNetworkMapResponse; +import org.apache.cloudstack.api.response.DnsZoneResponse; +import org.apache.cloudstack.api.response.ListResponse; + +import com.cloud.user.Account; +import com.cloud.utils.component.Manager; +import com.cloud.utils.component.PluggableService; + +public interface DnsProviderManager extends Manager, PluggableService { + + DnsServer addDnsServer(AddDnsServerCmd cmd); + ListResponse listDnsServers(ListDnsServersCmd cmd); + DnsServer updateDnsServer(UpdateDnsServerCmd cmd); + boolean deleteDnsServer(DeleteDnsServerCmd cmd); + DnsServerResponse createDnsServerResponse(DnsServer server); + + // Allocates the DB row (State: Inactive) + DnsZone allocateDnsZone(CreateDnsZoneCmd cmd); + // Calls the Plugin (State: Inactive -> Active) + DnsZone provisionDnsZone(long zoneId, boolean isImport); + + DnsZone updateDnsZone(UpdateDnsZoneCmd cmd); + boolean deleteDnsZone(Long id, boolean isUnmanage); + ListResponse listDnsZones(ListDnsZonesCmd cmd); + + DnsRecordResponse createDnsRecord(CreateDnsRecordCmd cmd); + boolean deleteDnsRecord(DeleteDnsRecordCmd cmd); + ListResponse listDnsRecords(ListDnsRecordsCmd cmd); + + List listProviderNames(); + + // Helper to create the response object + DnsZoneResponse createDnsZoneResponse(DnsZone zone); + DnsRecordResponse createDnsRecordResponse(DnsRecord record); + + DnsZoneNetworkMapResponse associateZoneToNetwork(AssociateDnsZoneToNetworkCmd cmd); + + boolean disassociateZoneFromNetwork(DisassociateDnsZoneFromNetworkCmd cmd); + + void checkDnsServerPermission(Account caller, DnsServer dnsServer); + + void checkDnsZonePermission(Account caller, DnsZone dnsZone); +} diff --git a/api/src/main/java/org/apache/cloudstack/dns/DnsProviderType.java b/api/src/main/java/org/apache/cloudstack/dns/DnsProviderType.java new file mode 100644 index 000000000000..23c8e936613f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/dns/DnsProviderType.java @@ -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. + +package org.apache.cloudstack.dns; + +public enum DnsProviderType { + PowerDNS; +// Cloudflare +} diff --git a/api/src/main/java/org/apache/cloudstack/dns/DnsRecord.java b/api/src/main/java/org/apache/cloudstack/dns/DnsRecord.java new file mode 100644 index 000000000000..ae62e4729cca --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/dns/DnsRecord.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.dns; + +import java.util.List; + +import com.cloud.utils.exception.CloudRuntimeException; + +public class DnsRecord { + + public enum RecordType { + A, AAAA, CNAME, MX, TXT, SRV, PTR, NS; + + public static RecordType fromString(String type) { + if (type == null) return null; + try { + return RecordType.valueOf(type.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new CloudRuntimeException("Invalid DNS Record Type: " + type + + ". Supported: " + java.util.Arrays.toString(values())); + } + } + } + + private String name; + private RecordType type; + private List contents; + private int ttl; + + public DnsRecord() {} + + public DnsRecord(String name, RecordType type, List contents, int ttl) { + this.name = name; + this.type = type; + this.contents = contents; + this.ttl = ttl; + } + + // Getters and Setters + public String getName() { return name; } + public void setName(String name) { this.name = name; } + + public RecordType getType() { return type; } + public void setType(RecordType type) { this.type = type; } + + public List getContents() { return contents; } + public void setContents(List contents) { this.contents = contents; } + + public int getTtl() { return ttl; } + public void setTtl(int ttl) { this.ttl = ttl; } +} diff --git a/api/src/main/java/org/apache/cloudstack/dns/DnsServer.java b/api/src/main/java/org/apache/cloudstack/dns/DnsServer.java new file mode 100644 index 000000000000..f34f94726d1b --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/dns/DnsServer.java @@ -0,0 +1,62 @@ +// 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.dns; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface DnsServer extends InternalIdentity, Identity, ControlledEntity { + enum State { + Enabled, Disabled + }; + + String getName(); + + String getUrl(); + + DnsProviderType getProviderType(); + + List getNameServers(); + + String getDnsApiKey(); + + long getAccountId(); + + boolean getPublicServer(); + + Date getCreated(); + + Date getRemoved(); + + String getPublicDomainSuffix(); + + Integer getPort(); + + Map getDetails(); + + String getDetail(String name); + + void setDetails(Map details); + + void appendDetails(String name, String value); +} diff --git a/api/src/main/java/org/apache/cloudstack/dns/DnsZone.java b/api/src/main/java/org/apache/cloudstack/dns/DnsZone.java new file mode 100644 index 000000000000..193185a6b0c6 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/dns/DnsZone.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.dns; + +import java.util.List; + +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface DnsZone extends InternalIdentity, Identity, ControlledEntity { + enum ZoneType { + Public, Private + } + enum State { + Active, Inactive + } + + String getName(); + + long getDnsServerId(); + + long getAccountId(); + + ZoneType getType(); + + String getDescription(); + + List getAssociatedNetworks(); + + State getState(); +} diff --git a/api/src/main/java/org/apache/cloudstack/dns/exception/DnsAuthenticationException.java b/api/src/main/java/org/apache/cloudstack/dns/exception/DnsAuthenticationException.java new file mode 100644 index 000000000000..325cb78241ef --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/dns/exception/DnsAuthenticationException.java @@ -0,0 +1,27 @@ +// 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.dns.exception; + +/** + * Thrown when authentication to the DNS provider fails. + */ +public class DnsAuthenticationException extends DnsProviderException { + public DnsAuthenticationException(String message) { + super(message); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/dns/exception/DnsConflictException.java b/api/src/main/java/org/apache/cloudstack/dns/exception/DnsConflictException.java new file mode 100644 index 000000000000..9a36bb87478a --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/dns/exception/DnsConflictException.java @@ -0,0 +1,27 @@ +// 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.dns.exception; + +/** + * Thrown when attempting to create a zone or record that already exists. + */ +public class DnsConflictException extends DnsProviderException { + public DnsConflictException(String message) { + super(message); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/dns/exception/DnsNotFoundException.java b/api/src/main/java/org/apache/cloudstack/dns/exception/DnsNotFoundException.java new file mode 100644 index 000000000000..aa88f308ce84 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/dns/exception/DnsNotFoundException.java @@ -0,0 +1,27 @@ +// 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.dns.exception; + +/** + * Thrown when the requested zone or record does not exist. + */ +public class DnsNotFoundException extends DnsProviderException { + public DnsNotFoundException(String message) { + super(message); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/dns/exception/DnsOperationException.java b/api/src/main/java/org/apache/cloudstack/dns/exception/DnsOperationException.java new file mode 100644 index 000000000000..564acdc9a6f0 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/dns/exception/DnsOperationException.java @@ -0,0 +1,27 @@ +// 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.dns.exception; + +/** + * Thrown for unexpected or unknown errors returned by the DNS provider. + */ +public class DnsOperationException extends DnsProviderException { + public DnsOperationException(String message) { + super(message); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/dns/exception/DnsProviderException.java b/api/src/main/java/org/apache/cloudstack/dns/exception/DnsProviderException.java new file mode 100644 index 000000000000..de307c9903e4 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/dns/exception/DnsProviderException.java @@ -0,0 +1,28 @@ +// 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.dns.exception; + +public class DnsProviderException extends Exception { + public DnsProviderException(String message) { + super(message); + } + + public DnsProviderException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/RoleTest.java b/api/src/main/java/org/apache/cloudstack/dns/exception/DnsTransportException.java similarity index 63% rename from framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/RoleTest.java rename to api/src/main/java/org/apache/cloudstack/dns/exception/DnsTransportException.java index 88265ee4e551..50f04143c921 100644 --- a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/RoleTest.java +++ b/api/src/main/java/org/apache/cloudstack/dns/exception/DnsTransportException.java @@ -15,20 +15,16 @@ // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.quota.activationrule.presetvariables; +package org.apache.cloudstack.dns.exception; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; +import java.io.IOException; -@RunWith(MockitoJUnitRunner.class) -public class RoleTest { +/** + * Thrown when HTTP or network errors occur communicating with the DNS provider. + */ +public class DnsTransportException extends DnsProviderException { - @Test - public void setTagsTestAddFieldTagsToCollection() { - Role variable = new Role(); - variable.setType(null); - Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("type")); + public DnsTransportException(String message, IOException cause) { + super(message, cause); } } diff --git a/api/src/main/java/org/apache/cloudstack/extension/CustomActionResultResponse.java b/api/src/main/java/org/apache/cloudstack/extension/CustomActionResultResponse.java index 33ff70fcace5..558340694b8a 100644 --- a/api/src/main/java/org/apache/cloudstack/extension/CustomActionResultResponse.java +++ b/api/src/main/java/org/apache/cloudstack/extension/CustomActionResultResponse.java @@ -62,4 +62,12 @@ public Boolean getSuccess() { public void setResult(Map result) { this.result = result; } + + public Map getResult() { + return result; + } + + public boolean isSuccess() { + return Boolean.TRUE.equals(success); + } } diff --git a/api/src/main/java/org/apache/cloudstack/extension/Extension.java b/api/src/main/java/org/apache/cloudstack/extension/Extension.java index 3068612ed6fe..c1d905718b24 100644 --- a/api/src/main/java/org/apache/cloudstack/extension/Extension.java +++ b/api/src/main/java/org/apache/cloudstack/extension/Extension.java @@ -24,7 +24,8 @@ public interface Extension extends InternalIdentity, Identity { enum Type { - Orchestrator + Orchestrator, + NetworkOrchestrator } enum State { Enabled, Disabled; diff --git a/api/src/main/java/org/apache/cloudstack/extension/ExtensionCustomAction.java b/api/src/main/java/org/apache/cloudstack/extension/ExtensionCustomAction.java index 776b42f671b7..245faa762a2f 100644 --- a/api/src/main/java/org/apache/cloudstack/extension/ExtensionCustomAction.java +++ b/api/src/main/java/org/apache/cloudstack/extension/ExtensionCustomAction.java @@ -48,7 +48,9 @@ public interface ExtensionCustomAction extends InternalIdentity, Identity { enum ResourceType { - VirtualMachine(com.cloud.vm.VirtualMachine.class); + VirtualMachine(com.cloud.vm.VirtualMachine.class), + Network(com.cloud.network.Network.class), + Vpc(com.cloud.network.vpc.Vpc.class); private final Class clazz; diff --git a/api/src/main/java/org/apache/cloudstack/extension/ExtensionHelper.java b/api/src/main/java/org/apache/cloudstack/extension/ExtensionHelper.java index f50f841ed74a..e3d84745f997 100644 --- a/api/src/main/java/org/apache/cloudstack/extension/ExtensionHelper.java +++ b/api/src/main/java/org/apache/cloudstack/extension/ExtensionHelper.java @@ -17,8 +17,126 @@ package org.apache.cloudstack.extension; +import java.util.List; +import java.util.Map; + +import com.cloud.network.Network.Capability; +import com.cloud.network.Network.Service; + public interface ExtensionHelper { Long getExtensionIdForCluster(long clusterId); Extension getExtension(long id); + Extension getExtensionByNameAndType(String name, Extension.Type type); Extension getExtensionForCluster(long clusterId); + List getExtensionReservedResourceDetails(long extensionId); + + /** + * Network Guru of Network Extension + */ + String NETWORK_EXTENSION_GURU_NAME = "NetworkExtensionGuestNetworkGuru"; + + /** + * Extension resource-map detail key holding the isolation method for the physical + * network (e.g. {@code "NetworkExtension"}). + * If not specified, the isolation method of physical network will be used. + */ + String NETWORK_ISOLATION_METHOD_DETAIL_KEY = "network.isolation.method"; + + /** + * Value of {@link #NETWORK_ISOLATION_METHOD_DETAIL_KEY} that indicates + * the extension owns the network isolation. + */ + String NETWORK_EXTENSION_ISOLATION_METHOD = "NetworkExtension"; + + /** + * Detail key used to store the comma-separated list of network services provided + * by a NetworkOrchestrator extension (e.g. {@code "SourceNat,StaticNat,Firewall"}). + */ + String NETWORK_SERVICES_DETAIL_KEY = "network.services"; + + /** + * Detail key used to store a JSON object mapping each service name to its + * CloudStack {@link com.cloud.network.Network.Capability} key/value pairs. + * Example: {@code {"SourceNat":{"SupportedSourceNatTypes":"peraccount"}}}. + * Used together with {@link #NETWORK_SERVICES_DETAIL_KEY}. + */ + String NETWORK_SERVICE_CAPABILITIES_DETAIL_KEY = "network.service.capabilities"; + + String getExtensionScriptPath(Extension extension); + + /** + * Finds the extension registered with the given physical network whose name + * matches the given provider name (case-insensitive). Returns {@code null} + * if no matching extension is found. + * + *

This is the preferred lookup when multiple extensions are registered on + * the same physical network: the provider name stored in + * {@code ntwk_service_map} is used to pinpoint the exact extension that + * handles a given network.

+ * + * @param physicalNetworkId the physical network ID + * @param providerName the provider name (must equal the extension name) + * @return the matching {@link Extension}, or {@code null} + */ + Extension getExtensionForPhysicalNetworkAndProvider(long physicalNetworkId, String providerName); + + /** + * Returns ALL {@code extension_resource_map_details} (including hidden) for + * the specific extension registered on the given physical network. Used by + * {@code NetworkExtensionElement} to inject device credentials into the script + * environment for the correct extension when multiple different extensions are + * registered on the same physical network. + * + * @param physicalNetworkId the physical network ID + * @param extensionId the extension ID + * @return all key/value details including non-display ones, or an empty map + */ + Map getAllResourceMapDetailsForExtensionOnPhysicalNetwork(long physicalNetworkId, long extensionId); + + /** + * Returns {@code true} if the given provider name is backed by a + * {@code NetworkOrchestrator} extension registered on any physical network. + * This is used by {@code NetworkModelImpl} to detect extension-backed providers + * that are not in the static {@code s_providerToNetworkElementMap}. + * + * @param providerName the provider / extension name + * @return true if the provider is a NetworkExtension provider + */ + boolean isNetworkExtensionProvider(String providerName); + + /** + * List all registered extensions filtered by extension {@link Extension.Type}. + * Useful for callers that need to discover available providers of a given + * type (e.g. Orchestrator, NetworkOrchestrator). + * + * @param type extension type to filter by + * @return list of matching {@link Extension} instances (empty list if none) + */ + List listExtensionsByType(Extension.Type type); + + /** + * Returns {@code true} when the extension registered for {@code providerName} on + * the given physical network has its {@link #NETWORK_ISOLATION_METHOD_DETAIL_KEY} + * resource-map detail set to {@link #NETWORK_EXTENSION_ISOLATION_METHOD}. + * + * @param providerName provider / extension name + * @return true if the extension uses NetworkExtension isolation + */ + boolean usesNetworkExtensionIsolation(String providerName); + + /** + * Returns the effective {@link Service} → ({@link Capability} → value) capabilities + * for the given external network provider, looking it up by name on the given + * physical network. + * + *

If {@code physicalNetworkId} is {@code null}, the method searches across all + * physical networks that have extensions registered and returns the capabilities for + * the first matching extension.

+ * + * @param physicalNetworkId physical network ID, or {@code null} for offering-level queries + * @param providerName provider / extension name + * @return capabilities map, or the default capabilities if no matching extension is found + */ + Map> getNetworkCapabilitiesForProvider(Long physicalNetworkId, String providerName); + } diff --git a/api/src/main/java/org/apache/cloudstack/extension/ExtensionResourceMap.java b/api/src/main/java/org/apache/cloudstack/extension/ExtensionResourceMap.java index 40ebc19eb5e3..604e64a1f894 100644 --- a/api/src/main/java/org/apache/cloudstack/extension/ExtensionResourceMap.java +++ b/api/src/main/java/org/apache/cloudstack/extension/ExtensionResourceMap.java @@ -24,7 +24,8 @@ public interface ExtensionResourceMap extends InternalIdentity, Identity { enum ResourceType { - Cluster + Cluster, + PhysicalNetwork } long getExtensionId(); diff --git a/api/src/main/java/org/apache/cloudstack/extension/NetworkCustomActionProvider.java b/api/src/main/java/org/apache/cloudstack/extension/NetworkCustomActionProvider.java new file mode 100644 index 000000000000..1c281c15f286 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/extension/NetworkCustomActionProvider.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.extension; + +import java.util.Map; + +import com.cloud.network.Network; +import com.cloud.network.vpc.Vpc; + +/** + * Implemented by network elements that support running custom actions on a + * managed network or VPC (e.g. NetworkExtensionElement). + * + *

This interface is looked up by {@code ExtensionsManagerImpl} to dispatch + * {@code runCustomAction} requests whose resource type is {@code Network} + * or {@code Vpc}.

+ */ +public interface NetworkCustomActionProvider { + + /** + * Returns {@code true} if this provider handles networks whose physical + * network has an ExternalNetwork service provider registered. + * + * @param network the target network + * @return {@code true} if this provider can handle the network + */ + boolean canHandleCustomAction(Network network); + + /** + * Returns {@code true} if this provider can handle custom actions for + * the given VPC. + * + * @param vpc the target VPC + * @return {@code true} if this provider can handle the VPC + */ + boolean canHandleVpcCustomAction(Vpc vpc); + + /** + * Runs a named custom action against the external network device that + * manages the given network. + * + * @param network the CloudStack network on which to run the action + * @param actionName the action name (e.g. {@code "reboot-device"}, {@code "dump-config"}) + * @param parameters optional parameters supplied by the caller + * @return output from the action script, or {@code null} on failure + */ + String runCustomAction(Network network, String actionName, Map parameters); + + /** + * Runs a named custom action against the external network device that + * manages the given VPC. + * + * @param vpc the CloudStack VPC on which to run the action + * @param actionName the action name + * @param parameters optional parameters supplied by the caller + * @return output from the action script, or {@code null} on failure + */ + String runCustomAction(Vpc vpc, String actionName, Map parameters); +} 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 index e54d53138ef6..cb3708cda373 100644 --- a/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoin.java +++ b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoin.java @@ -44,4 +44,6 @@ public interface GuiThemeJoin extends InternalIdentity, Identity { Date getCreated(); Date getRemoved(); + + String getLoginBaseDomain(); } diff --git a/api/src/main/java/org/apache/cloudstack/kms/HSMProfile.java b/api/src/main/java/org/apache/cloudstack/kms/HSMProfile.java new file mode 100644 index 000000000000..bbb8617ff24f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/kms/HSMProfile.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.kms; + +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +public interface HSMProfile extends Identity, InternalIdentity, ControlledEntity { + String getName(); + + String getProtocol(); + + long getAccountId(); + + long getDomainId(); + + Long getZoneId(); + + String getVendorName(); + + boolean isEnabled(); + + boolean getIsPublic(); + + Date getCreated(); + + Date getRemoved(); +} diff --git a/api/src/main/java/org/apache/cloudstack/kms/KMSKey.java b/api/src/main/java/org/apache/cloudstack/kms/KMSKey.java new file mode 100644 index 000000000000..c956a1ec66d8 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/kms/KMSKey.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.kms; + +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.framework.kms.KeyPurpose; + +import java.util.Date; + +/** + * KMS Key (Key Encryption Key) metadata. + * Represents a KEK that can be used to wrap/unwrap Data Encryption Keys (DEKs). + * KEKs are account-scoped and used for envelope encryption. + */ +public interface KMSKey extends Identity, InternalIdentity, ControlledEntity { + + String getName(); + + String getDescription(); + + /** + * Provider-specific KEK label/ID (internal identifier used by the KMS provider) + */ + String getKekLabel(); + + KeyPurpose getPurpose(); + + Long getZoneId(); + + String getAlgorithm(); + + Integer getKeyBits(); + + boolean isEnabled(); + + Date getCreated(); + + Date getRemoved(); + + Long getHsmProfileId(); +} diff --git a/api/src/main/java/org/apache/cloudstack/kms/KMSManager.java b/api/src/main/java/org/apache/cloudstack/kms/KMSManager.java new file mode 100644 index 000000000000..6e19010227d7 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/kms/KMSManager.java @@ -0,0 +1,292 @@ +// 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.kms; + +import com.cloud.storage.Volume; +import com.cloud.user.Account; +import com.cloud.utils.component.Manager; +import org.apache.cloudstack.api.command.admin.kms.MigrateVolumesToKMSCmd; +import org.apache.cloudstack.api.command.user.kms.RotateKMSKeyCmd; +import org.apache.cloudstack.api.command.user.kms.CreateKMSKeyCmd; +import org.apache.cloudstack.api.command.user.kms.DeleteKMSKeyCmd; +import org.apache.cloudstack.api.command.user.kms.ListKMSKeysCmd; +import org.apache.cloudstack.api.command.user.kms.UpdateKMSKeyCmd; +import org.apache.cloudstack.api.command.user.kms.hsm.CreateHSMProfileCmd; +import org.apache.cloudstack.api.command.user.kms.hsm.DeleteHSMProfileCmd; +import org.apache.cloudstack.api.command.user.kms.hsm.ListHSMProfilesCmd; +import org.apache.cloudstack.api.command.user.kms.hsm.UpdateHSMProfileCmd; +import org.apache.cloudstack.api.response.HSMProfileResponse; +import org.apache.cloudstack.api.response.KMSKeyResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; +import org.apache.cloudstack.framework.kms.KMSException; +import org.apache.cloudstack.framework.kms.KMSProvider; +import org.apache.cloudstack.framework.kms.WrappedKey; + +import java.util.List; + +public interface KMSManager extends Manager, Configurable { + + ConfigKey KMSDekSizeBits = new ConfigKey<>( + "Advanced", + Integer.class, + "kms.dek.size.bits", + "256", + "The size of Data Encryption Keys (DEK) in bits (128, 192, or 256)", + true, + ConfigKey.Scope.Global + ); + + ConfigKey KMSRetryCount = new ConfigKey<>( + "Advanced", + Integer.class, + "kms.retry.count", + "3", + "Number of retry attempts for transient KMS failures", + true, + ConfigKey.Scope.Global + ); + + ConfigKey KMSRetryDelayMs = new ConfigKey<>( + "Advanced", + Integer.class, + "kms.retry.delay.ms", + "1000", + "Delay in milliseconds between KMS retry attempts (exponential backoff)", + true, + ConfigKey.Scope.Global + ); + + ConfigKey KMSOperationTimeoutSec = new ConfigKey<>( + "Advanced", + Integer.class, + "kms.operation.timeout.sec", + "30", + "Timeout in seconds for KMS cryptographic operations", + true, + ConfigKey.Scope.Global + ); + + ConfigKey KMSRewrapBatchSize = new ConfigKey<>( + "Advanced", + Integer.class, + "kms.rewrap.batch.size", + "50", + "Number of wrapped keys to rewrap per batch in background job", + true, + ConfigKey.Scope.Global + ); + + ConfigKey KMSRewrapIntervalMs = new ConfigKey<>( + "Advanced", + Long.class, + "kms.rewrap.interval.ms", + "300000", + "Interval in milliseconds between background rewrap job executions (default: 5 minutes)", + true, + ConfigKey.Scope.Global + ); + + ConfigKey KMSOperationPoolCoreSize = new ConfigKey<>( + "Advanced", + Integer.class, + "kms.operation.pool.core.size", + "2", + "Minimum number of threads kept alive for KMS cryptographic operations", + true, + ConfigKey.Scope.Global + ); + + ConfigKey KMSOperationPoolMaxSize = new ConfigKey<>( + "Advanced", + Integer.class, + "kms.operation.pool.max.size", + "100", + "Maximum number of concurrent threads for KMS cryptographic operations. " + + "Set this to match the concurrency limit of your HSM appliance or external KMS provider.", + true, + ConfigKey.Scope.Global + ); + + /** + * List all registered KMS providers + * + * @return list of available providers + */ + List listKMSProviders(); + + /** + * Get a specific KMS provider by name + * + * @param name provider name + * @return the provider, or null if not found + */ + KMSProvider getKMSProvider(String name); + + /** + * Check if caller has permission to use a KMS key + * + * @param callerAccountId the caller's account ID + * @param key the KMS key + * @return true if caller has permission + */ + boolean hasPermission(Long callerAccountId, KMSKey key); + + /** + * Validates that the KMS key can be used for volume encryption: key exists, not deleted, + * owner account matches the volume owner, key state is Enabled, and key purpose is VOLUME_ENCRYPTION. + * No-op if kmsKeyId is null. + * + * @param owner the account that will own the volume + * @param kmsKeyId the KMS key database ID + * @param zoneId the zone ID of the target resource (volume/VM) + * @throws InvalidParameterValueException if key not found, disabled, wrong purpose, zone mismatch, or account mismatch + */ + void checkKmsKeyForVolumeEncryption(Account owner, Long kmsKeyId, Long zoneId); + + /** + * Unwrap a DEK by wrapped key ID, trying multiple KEK versions if needed + * + * @param wrappedKeyId the wrapped key database ID + * @return plaintext DEK (caller must zeroize!) + * @throws KMSException if unwrap fails + */ + byte[] unwrapKey(Long wrappedKeyId) throws KMSException; + + /** + * Generate and wrap a DEK using a specific KMS key UUID + * + * @param kmsKey the KMS key + * @param callerAccountId the caller's account ID + * @return wrapped key ready for database storage + * @throws KMSException if operation fails + */ + WrappedKey generateVolumeKeyWithKek(KMSKey kmsKey, Long callerAccountId) throws KMSException; + + /** + * Create a KMS key and return the response object. + * Handles validation, account resolution, and permission checks. + * + * @param cmd the create command with all parameters + * @return KMSKeyResponse + * @throws KMSException if creation fails + */ + KMSKeyResponse createKMSKey(CreateKMSKeyCmd cmd) throws KMSException; + + /** + * List KMS keys and return the response object. + * Handles validation and permission checks. + * + * @param cmd the list command with all parameters + * @return ListResponse with KMSKeyResponse objects + */ + ListResponse listKMSKeys(ListKMSKeysCmd cmd); + + /** + * Update a KMS key and return the response object. + * Handles validation and permission checks. + * + * @param cmd the update command with all parameters + * @return KMSKeyResponse + * @throws KMSException if update fails + */ + KMSKeyResponse updateKMSKey(UpdateKMSKeyCmd cmd) throws KMSException; + + boolean deleteKMSWrappedKey(Volume vol) throws KMSException; + + /** + * Delete a KMS key and return the response object. + * Handles validation and permission checks. + * + * @param cmd the delete command with all parameters + * @return SuccessResponse + * @throws KMSException if deletion fails + */ + SuccessResponse deleteKMSKey(DeleteKMSKeyCmd cmd) throws KMSException; + + /** + * Rotate KEK by creating new version and scheduling gradual re-encryption + * + * @param cmd the rotate command with all parameters + * @return New KEK version UUID + * @throws KMSException if rotation fails + */ + String rotateKMSKey(RotateKMSKeyCmd cmd) throws KMSException; + + /** + * Migrate passphrase-based volumes to KMS encryption + * + * @param cmd the migrate command with all parameters + * @return Number of volumes successfully migrated + * @throws KMSException if migration fails + */ + int migrateVolumesToKMS(MigrateVolumesToKMSCmd cmd) throws KMSException; + + /** + * Delete all KMS keys owned by an account (called during account cleanup) + * + * @param accountId the account ID + * @return true if all keys were successfully deleted + */ + boolean deleteKMSKeysByAccountId(Long accountId); + + /** + * Add a new HSM profile + * + * @param cmd the add command + * @return the created HSM profile + * @throws KMSException if addition fails + */ + HSMProfile addHSMProfile(CreateHSMProfileCmd cmd) throws KMSException; + + /** + * List HSM profiles + * + * @param cmd the list command + * @return list of HSM profiles + */ + ListResponse listHSMProfiles(ListHSMProfilesCmd cmd); + + /** + * Delete an HSM profile + * + * @param cmd the delete command + * @return true if deletion was successful + * @throws KMSException if deletion fails + */ + boolean deleteHSMProfile(DeleteHSMProfileCmd cmd) throws KMSException; + + /** + * Update an HSM profile + * + * @param cmd the update command + * @return the updated HSM profile + * @throws KMSException if update fails + */ + HSMProfile updateHSMProfile(UpdateHSMProfileCmd cmd) throws KMSException; + + /** + * Create a response object for an HSM profile + * + * @param profile the HSM profile + * @return the response object + */ + HSMProfileResponse createHSMProfileResponse(HSMProfile profile); +} 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 6a20c2fa2486..b6362e9a9c9b 100644 --- a/api/src/main/java/org/apache/cloudstack/query/QueryService.java +++ b/api/src/main/java/org/apache/cloudstack/query/QueryService.java @@ -41,6 +41,7 @@ import org.apache.cloudstack.api.command.user.account.ListProjectAccountsCmd; import org.apache.cloudstack.api.command.user.address.ListQuarantinedIpsCmd; import org.apache.cloudstack.api.command.user.affinitygroup.ListAffinityGroupsCmd; +import org.apache.cloudstack.api.command.user.backup.ListBackupServiceJobsCmd; import org.apache.cloudstack.api.command.user.bucket.ListBucketsCmd; import org.apache.cloudstack.api.command.user.event.ListEventsCmd; import org.apache.cloudstack.api.command.user.iso.ListIsosCmd; @@ -63,6 +64,7 @@ import org.apache.cloudstack.api.command.user.zone.ListZonesCmd; import org.apache.cloudstack.api.response.AccountResponse; import org.apache.cloudstack.api.response.AsyncJobResponse; +import org.apache.cloudstack.api.response.BackupServiceJobResponse; import org.apache.cloudstack.api.response.BucketResponse; import org.apache.cloudstack.api.response.DetailOptionsResponse; import org.apache.cloudstack.api.response.DiskOfferingResponse; @@ -119,8 +121,8 @@ public interface QueryService { "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); 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); + "user.vm.readonly.details", "Advanced", "dataDiskController, rootDiskController, backupValidationCommandTimeout, backupValidationScreenshotWait, backupValidationBootTimeout", + "List of read-only VM settings/details as comma separated string", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.CSV, null, ""); ConfigKey SortKeyAscending = new ConfigKey<>("Advanced", Boolean.class, "sortkey.algorithm", "true", "Sort algorithm - ascending or descending - to use. For entities that use sort key(template, disk offering, service offering, " + @@ -132,10 +134,10 @@ public interface QueryService { "Determines whether users can view all user accounts within the same domain", true, ConfigKey.Scope.Domain); 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); + "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); + "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); @@ -145,6 +147,8 @@ public interface QueryService { ListResponse searchForUsers(Long domainId, boolean recursive) throws PermissionDeniedException; + List searchForAccessibleUsers(); + ListResponse searchForEvents(ListEventsCmd cmd); ListResponse listTags(ListTagsCmd cmd); @@ -222,4 +226,6 @@ public interface QueryService { ListResponse searchForObjectStores(ListObjectStoragePoolsCmd listObjectStoragePoolsCmd); ListResponse searchForBuckets(ListBucketsCmd listBucketsCmd); + + ListResponse listBackupServiceJobs(ListBackupServiceJobsCmd cmd); } diff --git a/api/src/main/java/org/apache/cloudstack/resourcelimit/Reserver.java b/api/src/main/java/org/apache/cloudstack/resourcelimit/Reserver.java new file mode 100644 index 000000000000..6b3f57b6aa5e --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/resourcelimit/Reserver.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.resourcelimit; + +/** + * Interface implemented by CheckedReservation. + *

+ * This is defined in cloud-api to allow methods declared in modules that do not depend on cloud-server + * to receive CheckedReservations as parameters. + */ +public interface Reserver extends AutoCloseable { + + void close(); + +} diff --git a/api/src/main/java/org/apache/cloudstack/vm/schedule/VMSchedule.java b/api/src/main/java/org/apache/cloudstack/schedule/ResourceSchedule.java similarity index 66% rename from api/src/main/java/org/apache/cloudstack/vm/schedule/VMSchedule.java rename to api/src/main/java/org/apache/cloudstack/schedule/ResourceSchedule.java index b1e7df3c39cf..4bd39211a27f 100644 --- a/api/src/main/java/org/apache/cloudstack/vm/schedule/VMSchedule.java +++ b/api/src/main/java/org/apache/cloudstack/schedule/ResourceSchedule.java @@ -16,20 +16,31 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.cloudstack.vm.schedule; +package org.apache.cloudstack.schedule; +import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; import java.time.ZoneId; import java.util.Date; -public interface VMSchedule extends Identity, InternalIdentity { - enum Action { - START, STOP, REBOOT, FORCE_STOP, FORCE_REBOOT +public interface ResourceSchedule extends Identity, InternalIdentity { + + /** + * Common contract for scheduler actions. Each provider defines its own enum + * implementing this interface so the generic machinery can call {@link #name()} + * and {@link #getEventType()} without knowing the concrete type. + */ + interface Action { + String name(); + + String getEventType(); } - long getVmId(); + ApiCommandResourceType getResourceType(); + + long getResourceId(); String getDescription(); @@ -37,7 +48,7 @@ enum Action { String getTimeZone(); - Action getAction(); + String getActionName(); boolean getEnabled(); diff --git a/api/src/main/java/org/apache/cloudstack/schedule/ResourceScheduleManager.java b/api/src/main/java/org/apache/cloudstack/schedule/ResourceScheduleManager.java new file mode 100644 index 000000000000..fb650a6cf68d --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/schedule/ResourceScheduleManager.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.schedule; + +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ResourceScheduleResponse; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +public interface ResourceScheduleManager { + + ResourceScheduleResponse createSchedule(ApiCommandResourceType resourceType, String resourceUuid, + String description, String schedule, String timeZone, String action, + Date startDate, Date endDate, boolean enabled, Map details); + + ResourceScheduleResponse updateSchedule(Long id, String description, String schedule, String timeZone, + Date startDate, Date endDate, Boolean enabled, Map details); + + ListResponse listSchedule(Long id, List ids, ApiCommandResourceType resourceType, + String resourceUuid, String action, Boolean enabled, + Long startIndex, Long pageSize); + + Long removeSchedule(ApiCommandResourceType resourceType, String resourceUuid, Long id, List ids); + + void removeSchedulesForResource(ApiCommandResourceType resourceType, long resourceId); +} diff --git a/api/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduledJob.java b/api/src/main/java/org/apache/cloudstack/schedule/ResourceScheduledJob.java similarity index 77% rename from api/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduledJob.java rename to api/src/main/java/org/apache/cloudstack/schedule/ResourceScheduledJob.java index d7a18b768279..a7bf4d9aa05e 100644 --- a/api/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduledJob.java +++ b/api/src/main/java/org/apache/cloudstack/schedule/ResourceScheduledJob.java @@ -16,23 +16,26 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.cloudstack.vm.schedule; +package org.apache.cloudstack.schedule; +import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; import java.util.Date; -public interface VMScheduledJob extends Identity, InternalIdentity { - long getVmId(); +public interface ResourceScheduledJob extends Identity, InternalIdentity { + ApiCommandResourceType getResourceType(); - long getVmScheduleId(); + long getResourceId(); + + long getScheduleId(); Long getAsyncJobId(); void setAsyncJobId(long asyncJobId); - VMSchedule.Action getAction(); + String getActionName(); Date getScheduledTime(); } diff --git a/api/src/main/java/org/apache/cloudstack/schedule/autoscale/AutoScaleScheduleAction.java b/api/src/main/java/org/apache/cloudstack/schedule/autoscale/AutoScaleScheduleAction.java new file mode 100644 index 000000000000..c05dafe88796 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/schedule/autoscale/AutoScaleScheduleAction.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.schedule.autoscale; + +import com.cloud.event.EventTypes; +import org.apache.cloudstack.schedule.ResourceSchedule; + +public enum AutoScaleScheduleAction implements ResourceSchedule.Action { + UPDATE { + @Override + public String getEventType() { + return EventTypes.EVENT_AUTOSCALEVMGROUP_SCHEDULE_UPDATE; + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/schedule/vm/VMScheduleAction.java b/api/src/main/java/org/apache/cloudstack/schedule/vm/VMScheduleAction.java new file mode 100644 index 000000000000..1648305803ff --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/schedule/vm/VMScheduleAction.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.schedule.vm; + +import com.cloud.event.EventTypes; +import org.apache.cloudstack.schedule.ResourceSchedule; + +public enum VMScheduleAction implements ResourceSchedule.Action { + START { + @Override + public String getEventType() { return EventTypes.EVENT_VM_SCHEDULE_START; } + }, + STOP { + @Override + public String getEventType() { return EventTypes.EVENT_VM_SCHEDULE_STOP; } + }, + REBOOT { + @Override + public String getEventType() { return EventTypes.EVENT_VM_SCHEDULE_REBOOT; } + }, + FORCE_STOP { + @Override + public String getEventType() { return EventTypes.EVENT_VM_SCHEDULE_FORCE_STOP; } + }, + FORCE_REBOOT { + @Override + public String getEventType() { return EventTypes.EVENT_VM_SCHEDULE_FORCE_REBOOT; } + }; +} diff --git a/api/src/main/java/org/apache/cloudstack/secstorage/heuristics/HeuristicType.java b/api/src/main/java/org/apache/cloudstack/secstorage/heuristics/HeuristicType.java index f23e4b0b633b..489e9bf54f23 100644 --- a/api/src/main/java/org/apache/cloudstack/secstorage/heuristics/HeuristicType.java +++ b/api/src/main/java/org/apache/cloudstack/secstorage/heuristics/HeuristicType.java @@ -18,8 +18,8 @@ /** * The type of the heuristic used in the allocation process of secondary storage resources. - * Valid options are: {@link #ISO}, {@link #SNAPSHOT}, {@link #TEMPLATE} and {@link #VOLUME} + * Valid options are: {@link #ISO}, {@link #SNAPSHOT}, {@link #TEMPLATE}, {@link #VOLUME} and {@link #BACKUP} */ public enum HeuristicType { - ISO, SNAPSHOT, TEMPLATE, VOLUME + ISO, SNAPSHOT, TEMPLATE, VOLUME, BACKUP } diff --git a/api/src/main/java/org/apache/cloudstack/storage/object/BucketApiService.java b/api/src/main/java/org/apache/cloudstack/storage/object/BucketApiService.java index e27ef308d7f2..8c164133db88 100644 --- a/api/src/main/java/org/apache/cloudstack/storage/object/BucketApiService.java +++ b/api/src/main/java/org/apache/cloudstack/storage/object/BucketApiService.java @@ -95,7 +95,7 @@ public interface BucketApiService { */ Bucket createBucket(CreateBucketCmd cmd); - boolean deleteBucket(long bucketId, Account caller); + boolean deleteBucket(long bucketId, Account caller) throws ResourceAllocationException; boolean updateBucket(UpdateBucketCmd cmd, Account caller) throws ResourceAllocationException; diff --git a/api/src/main/java/org/apache/cloudstack/storage/sharedfs/SharedFSService.java b/api/src/main/java/org/apache/cloudstack/storage/sharedfs/SharedFSService.java index 21184de27a26..3e349cf0bd39 100644 --- a/api/src/main/java/org/apache/cloudstack/storage/sharedfs/SharedFSService.java +++ b/api/src/main/java/org/apache/cloudstack/storage/sharedfs/SharedFSService.java @@ -23,6 +23,10 @@ import org.apache.cloudstack.api.command.user.storage.sharedfs.ChangeSharedFSServiceOfferingCmd; import org.apache.cloudstack.api.command.user.storage.sharedfs.CreateSharedFSCmd; import org.apache.cloudstack.api.command.user.storage.sharedfs.DestroySharedFSCmd; +import org.apache.cloudstack.api.command.user.storage.sharedfs.ListSharedFSCmd; +import org.apache.cloudstack.api.command.user.storage.sharedfs.UpdateSharedFSCmd; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.SharedFSResponse; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.ManagementServerException; @@ -31,11 +35,6 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.VirtualMachineMigrationException; -import org.apache.cloudstack.api.command.user.storage.sharedfs.ListSharedFSCmd; -import org.apache.cloudstack.api.command.user.storage.sharedfs.UpdateSharedFSCmd; -import org.apache.cloudstack.api.response.SharedFSResponse; -import org.apache.cloudstack.api.response.ListResponse; - public interface SharedFSService { List getSharedFSProviders(); @@ -69,4 +68,10 @@ public interface SharedFSService { SharedFS recoverSharedFS(Long sharedFSId); void deleteSharedFS(Long sharedFSId); + + SharedFS getSharedFSByUuid(String uuid); + + SharedFS getSharedFSForVmId(long vmId); + + SharedFS updateSharedFSPostRestore(long sharedFsId, long volumeId); } diff --git a/api/src/main/java/org/apache/cloudstack/storage/template/VnfTemplateManager.java b/api/src/main/java/org/apache/cloudstack/storage/template/VnfTemplateManager.java index 6571346ad654..3df59811561b 100644 --- a/api/src/main/java/org/apache/cloudstack/storage/template/VnfTemplateManager.java +++ b/api/src/main/java/org/apache/cloudstack/storage/template/VnfTemplateManager.java @@ -29,6 +29,7 @@ import org.apache.cloudstack.api.command.user.vm.DeployVnfApplianceCmd; import org.apache.cloudstack.framework.config.ConfigKey; import java.util.List; +import java.util.Map; public interface VnfTemplateManager { @@ -42,11 +43,12 @@ public interface VnfTemplateManager { void updateVnfTemplate(long templateId, UpdateVnfTemplateCmd cmd); - void validateVnfApplianceNics(VirtualMachineTemplate template, List networkIds); + void validateVnfApplianceNics(VirtualMachineTemplate template, List networkIds, Map vmNetworkMap); SecurityGroup createSecurityGroupForVnfAppliance(DataCenter zone, VirtualMachineTemplate template, Account owner, DeployVnfApplianceCmd cmd); void createIsolatedNetworkRulesForVnfAppliance(DataCenter zone, VirtualMachineTemplate template, Account owner, UserVm vm, DeployVnfApplianceCmd cmd) throws InsufficientAddressCapacityException, ResourceAllocationException, ResourceUnavailableException; + } diff --git a/api/src/main/java/org/apache/cloudstack/storage/template/VnfTemplateUtils.java b/api/src/main/java/org/apache/cloudstack/storage/template/VnfTemplateUtils.java index e997a50cec03..16ff2abb564a 100644 --- a/api/src/main/java/org/apache/cloudstack/storage/template/VnfTemplateUtils.java +++ b/api/src/main/java/org/apache/cloudstack/storage/template/VnfTemplateUtils.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.storage.template; +import com.cloud.agent.api.to.deployasis.OVFNetworkTO; import com.cloud.exception.InvalidParameterValueException; import com.cloud.network.VNF; import com.cloud.storage.Storage; @@ -124,6 +125,9 @@ public static void validateVnfNics(List nicsList) { public static void validateApiCommandParams(BaseCmd cmd, VirtualMachineTemplate template) { if (cmd instanceof RegisterVnfTemplateCmd) { RegisterVnfTemplateCmd registerCmd = (RegisterVnfTemplateCmd) cmd; + if (registerCmd.isDeployAsIs() && CollectionUtils.isNotEmpty(registerCmd.getVnfNics())) { + throw new InvalidParameterValueException("VNF nics cannot be specified when register a deploy-as-is Template. Please wait until Template settings are read from OVA."); + } validateApiCommandParams(registerCmd.getVnfDetails(), registerCmd.getVnfNics(), registerCmd.getTemplateType()); } else if (cmd instanceof UpdateVnfTemplateCmd) { UpdateVnfTemplateCmd updateCmd = (UpdateVnfTemplateCmd) cmd; @@ -149,4 +153,18 @@ public static void validateVnfCidrList(List cidrList) { } } } + + public static void validateDeployAsIsTemplateVnfNics(List ovfNetworks, List vnfNics) { + if (CollectionUtils.isEmpty(vnfNics)) { + return; + } + if (CollectionUtils.isEmpty(ovfNetworks)) { + throw new InvalidParameterValueException("The list of networks read from OVA is empty. Please wait until the template is fully downloaded and processed."); + } + for (VNF.VnfNic vnfNic : vnfNics) { + if (vnfNic.getDeviceId() < ovfNetworks.size() && !vnfNic.isRequired()) { + throw new InvalidParameterValueException(String.format("The VNF nic [device ID: %s ] is required as it is defined in the OVA template.", vnfNic.getDeviceId())); + } + } + } } diff --git a/api/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageService.java b/api/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageService.java index 5f69f3e46e73..13f01b4944ad 100644 --- a/api/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageService.java +++ b/api/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageService.java @@ -25,17 +25,28 @@ import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.VolumeForImportResponse; import org.apache.cloudstack.api.response.VolumeResponse; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; import java.util.Arrays; import java.util.List; -public interface VolumeImportUnmanageService extends PluggableService { +public interface VolumeImportUnmanageService extends PluggableService, Configurable { List SUPPORTED_HYPERVISORS = Arrays.asList(Hypervisor.HypervisorType.KVM, Hypervisor.HypervisorType.VMware); List SUPPORTED_STORAGE_POOL_TYPES_FOR_KVM = Arrays.asList(Storage.StoragePoolType.NetworkFilesystem, - Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.RBD); + Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.RBD, Storage.StoragePoolType.SharedMountPoint); + + ConfigKey AllowImportVolumeWithBackingFile = new ConfigKey<>(Boolean.class, + "allow.import.volume.with.backing.file", + "Advanced", + "false", + "If enabled, allows QCOW2 volumes with backing files to be imported or unmanaged", + true, + ConfigKey.Scope.Global, + null); ListResponse listVolumesForImport(ListVolumesForImportCmd cmd); diff --git a/api/src/main/java/org/apache/cloudstack/userdata/UserDataManager.java b/api/src/main/java/org/apache/cloudstack/userdata/UserDataManager.java index b4bede248907..7e718413118e 100644 --- a/api/src/main/java/org/apache/cloudstack/userdata/UserDataManager.java +++ b/api/src/main/java/org/apache/cloudstack/userdata/UserDataManager.java @@ -22,6 +22,8 @@ import com.cloud.utils.component.Manager; +import java.io.IOException; + public interface UserDataManager extends Manager, Configurable { String VM_USERDATA_MAX_LENGTH_STRING = "vm.userdata.max.length"; ConfigKey VM_USERDATA_MAX_LENGTH = new ConfigKey<>("Advanced", Integer.class, VM_USERDATA_MAX_LENGTH_STRING, "32768", @@ -29,4 +31,14 @@ public interface UserDataManager extends Manager, Configurable { String concatenateUserData(String userdata1, String userdata2, String userdataProvider); String validateUserData(String userData, BaseCmd.HTTPMethod httpmethod); + + /** + * This method validates the user data uuid for system VMs and returns the user data + * after compression and base64 encoding for the system VM to consume. + * + * @param userDataUuid + * @return a String containing the user data after compression and base64 encoding + * @throws IOException + */ + String validateAndGetUserDataForSystemVM(String userDataUuid) throws IOException; } diff --git a/api/src/main/java/org/apache/cloudstack/vm/ImportVmTask.java b/api/src/main/java/org/apache/cloudstack/vm/ImportVmTask.java new file mode 100644 index 000000000000..5bb51dfcbcb9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/vm/ImportVmTask.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 org.apache.cloudstack.vm; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface ImportVmTask extends Identity, InternalIdentity { + enum Step { + Prepare, CloningInstance, ConvertingInstance, Importing, Completed + } + + enum TaskState { + Running, Completed, Failed; + + public static TaskState getValue(String state) { + for (TaskState s : TaskState.values()) { + if (s.name().equalsIgnoreCase(state)) { + return s; + } + } + throw new IllegalArgumentException("Invalid task state: " + state); + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/vm/ImportVmTasksManager.java b/api/src/main/java/org/apache/cloudstack/vm/ImportVmTasksManager.java new file mode 100644 index 000000000000..233511fbce8d --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/vm/ImportVmTasksManager.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.vm; + +import com.cloud.dc.DataCenter; +import com.cloud.host.Host; +import com.cloud.user.Account; +import org.apache.cloudstack.api.command.admin.vm.ListImportVMTasksCmd; +import org.apache.cloudstack.api.response.ImportVMTaskResponse; +import org.apache.cloudstack.api.response.ListResponse; + +public interface ImportVmTasksManager { + + ListResponse listImportVMTasks(ListImportVMTasksCmd cmd); + + ImportVmTask createImportVMTaskRecord(DataCenter zone, Account owner, long userId, String displayName, + String vcenter, String datacenterName, String sourceVMName, + Host convertHost, Host importHost); + + void updateImportVMTaskStep(ImportVmTask importVMTaskVO, DataCenter zone, Account owner, Host convertHost, + Host importHost, Long vmId, ImportVmTask.Step step); + + void updateImportVMTaskErrorState(ImportVmTask importVMTaskVO, ImportVmTask.TaskState state, String errorMsg); +} diff --git a/api/src/main/java/org/apache/cloudstack/vm/UnmanageVMService.java b/api/src/main/java/org/apache/cloudstack/vm/UnmanageVMService.java index 2315e5f2e95f..70d8795f3058 100644 --- a/api/src/main/java/org/apache/cloudstack/vm/UnmanageVMService.java +++ b/api/src/main/java/org/apache/cloudstack/vm/UnmanageVMService.java @@ -17,11 +17,14 @@ package org.apache.cloudstack.vm; +import com.cloud.utils.Pair; + public interface UnmanageVMService { /** * Unmanage a guest VM from CloudStack - * @return true if the VM is successfully unmanaged, false if not. + * + * @return (true if successful, false if not, hostUuid) if the VM is successfully unmanaged. */ - boolean unmanageVMInstance(long vmId); + Pair unmanageVMInstance(long vmId, Long paramHostId, boolean isForced); } 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 bba97dff71cb..cbb7c4de698a 100644 --- a/api/src/main/java/org/apache/cloudstack/vm/UnmanagedInstanceTO.java +++ b/api/src/main/java/org/apache/cloudstack/vm/UnmanagedInstanceTO.java @@ -55,6 +55,9 @@ public enum PowerState { private String hostName; + private String hypervisorType; + private String hostHypervisorVersion; + private List disks; private List nics; @@ -168,6 +171,22 @@ public void setHostName(String hostName) { this.hostName = hostName; } + public String getHypervisorType() { + return hypervisorType; + } + + public void setHypervisorType(String hypervisorType) { + this.hypervisorType = hypervisorType; + } + + public String getHostHypervisorVersion() { + return hostHypervisorVersion; + } + + public void setHostHypervisorVersion(String hostHypervisorVersion) { + this.hostHypervisorVersion = hostHypervisorVersion; + } + public List getDisks() { return disks; } diff --git a/api/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManager.java b/api/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManager.java index b6233b9c2704..e1963313eb6f 100644 --- a/api/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManager.java +++ b/api/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManager.java @@ -26,6 +26,8 @@ public interface UnmanagedVMsManager extends VmImportService, UnmanageVMService, PluggableService, Configurable { + String VM_IMPORT_DEFAULT_TEMPLATE_NAME = "system-default-vm-import-dummy-template.iso"; + String KVM_VM_IMPORT_DEFAULT_TEMPLATE_NAME = "kvm-default-vm-import-dummy-template"; ConfigKey UnmanageVMPreserveNic = new ConfigKey<>("Advanced", Boolean.class, "unmanage.vm.preserve.nics", "false", "If set to true, do not remove VM nics (and its MAC addresses) when unmanaging a VM, leaving them allocated but not reserved. " + "If set to false, nics are removed and MAC addresses can be reassigned", true, ConfigKey.Scope.Zone); diff --git a/api/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduleManager.java b/api/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduleManager.java deleted file mode 100644 index 6aca05b58d5e..000000000000 --- a/api/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduleManager.java +++ /dev/null @@ -1,40 +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.vm.schedule; - -import org.apache.cloudstack.api.command.user.vm.CreateVMScheduleCmd; -import org.apache.cloudstack.api.command.user.vm.DeleteVMScheduleCmd; -import org.apache.cloudstack.api.command.user.vm.ListVMScheduleCmd; -import org.apache.cloudstack.api.command.user.vm.UpdateVMScheduleCmd; -import org.apache.cloudstack.api.response.ListResponse; -import org.apache.cloudstack.api.response.VMScheduleResponse; - -public interface VMScheduleManager { - VMScheduleResponse createSchedule(CreateVMScheduleCmd createVMScheduleCmd); - - VMScheduleResponse createResponse(VMSchedule vmSchedule); - - ListResponse listSchedule(ListVMScheduleCmd listVMScheduleCmd); - - VMScheduleResponse updateSchedule(UpdateVMScheduleCmd updateVMScheduleCmd); - - long removeScheduleByVmId(long vmId, boolean expunge); - - Long removeSchedule(DeleteVMScheduleCmd deleteVMScheduleCmd); -} diff --git a/api/src/test/java/com/cloud/agent/api/storage/OVFHelperTest.java b/api/src/test/java/com/cloud/agent/api/storage/OVFHelperTest.java index c8f7ef3ec9ba..5192f182a524 100644 --- a/api/src/test/java/com/cloud/agent/api/storage/OVFHelperTest.java +++ b/api/src/test/java/com/cloud/agent/api/storage/OVFHelperTest.java @@ -40,7 +40,7 @@ public class OVFHelperTest { "This will enable the SSHD service and configure the specified public key" + "" + "" + - "" + + "" + "This allows to pass any text to the appliance. The value should be encoded in base64" + "" + ""; @@ -576,15 +576,15 @@ public class OVFHelperTest { " SSO Configuration\n" + " \n" + " \n" + - " For the first instance of the identity domain, this is the username with Administrator privileges. Otherwise, this is the username of the replication partner.\n" + + " For the first Instance of the identity domain, this is the username with Administrator privileges. Otherwise, this is the username of the replication partner.\n" + " \n" + " \n" + " \n" + - " For the first instance of the identity domain, this is the password given to the Administrator account. Otherwise, this is the password of the Administrator account of the replication partner.\n" + + " For the first Instance of the identity domain, this is the password given to the Administrator account. Otherwise, this is the password of the Administrator account of the replication partner.\n" + " \n" + " \n" + " \n" + - " For the first instance of the identity domain, this is the name of the newly created domain.\n" + + " For the first Instance of the identity domain, this is the name of the newly created domain.\n" + " \n" + " \n" + " \n" + @@ -592,11 +592,11 @@ public class OVFHelperTest { " \n" + " \n" + " \n" + - " If this parameter is set to True, the VMware directory instance is setup as the first instance of a new identity domain. Otherwise, the instance is setup as a replication partner.\n" + + " If this parameter is set to True, the VMware directory Instance is setup as the first Instance of a new identity domain. Otherwise, the Instance is setup as a replication partner.\n" + " \n" + " \n" + " \n" + - " The hostname of the VMware directory replication partner. This value is ignored for the first instance of the identity domain.\n" + + " The hostname of the VMware directory replication partner. This value is ignored for the first Instance of the identity domain.\n" + " \n" + " Database Configuration\n" + " \n" + @@ -625,7 +625,7 @@ public class OVFHelperTest { " \n" + " \n" + " \n" + - " String describing the external database instance. Values could be anything depending on what the database instance name the DBA creates in the external db. (ignored when the db.type is 'embedded').\n" + + " String describing the external database Instance. Values could be anything depending on what the database Instance name the DBA creates in the external db. (ignored when the db.type is 'embedded').\n" + " \n" + " System Configuration\n" + " \n" + diff --git a/api/src/test/java/com/cloud/cpu/CPUTest.java b/api/src/test/java/com/cloud/cpu/CPUTest.java index dfedf21864cc..0a059cf9a90a 100644 --- a/api/src/test/java/com/cloud/cpu/CPUTest.java +++ b/api/src/test/java/com/cloud/cpu/CPUTest.java @@ -28,6 +28,7 @@ public void testCPUArchGetType() { assertEquals("i686", CPU.CPUArch.x86.getType()); assertEquals("x86_64", CPU.CPUArch.amd64.getType()); assertEquals("aarch64", CPU.CPUArch.arm64.getType()); + assertEquals("s390x", CPU.CPUArch.s390x.getType()); } @Test @@ -35,6 +36,7 @@ public void testCPUArchGetBits() { assertEquals(32, CPU.CPUArch.x86.getBits()); assertEquals(64, CPU.CPUArch.amd64.getBits()); assertEquals(64, CPU.CPUArch.arm64.getBits()); + assertEquals(64, CPU.CPUArch.s390x.getBits()); } @Test @@ -42,6 +44,7 @@ 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")); + assertEquals(CPU.CPUArch.s390x, CPU.CPUArch.fromType("s390x")); } @Test @@ -61,7 +64,7 @@ public void testCPUArchFromTypeWithInvalidValue() { @Test public void testCPUArchGetTypesAsCSV() { - String expectedCSV = "i686,x86_64,aarch64"; + String expectedCSV = "i686,x86_64,aarch64,s390x"; assertEquals(expectedCSV, CPU.CPUArch.getTypesAsCSV()); } } diff --git a/api/src/test/java/com/cloud/network/NetworkTest.java b/api/src/test/java/com/cloud/network/NetworkTest.java new file mode 100644 index 000000000000..dba4d1fb7eb5 --- /dev/null +++ b/api/src/test/java/com/cloud/network/NetworkTest.java @@ -0,0 +1,73 @@ +// 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 org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + + +public class NetworkTest { + + @Test + public void testProviderContains() { + List providers = new ArrayList<>(); + providers.add(Network.Provider.VirtualRouter); + + // direct instance present + assertTrue("List should contain VirtualRouter provider", providers.contains(Network.Provider.VirtualRouter)); + + // resolved provider by name (registered provider) + Network.Provider resolved = Network.Provider.getProvider("VirtualRouter"); + assertNotNull("Resolved provider should not be null", resolved); + assertTrue("List should contain resolved VirtualRouter provider", providers.contains(resolved)); + + // transient provider with same name should be considered equal (equals by name) + Network.Provider transientProvider = Network.Provider.createTransientProvider("NetworkExtension"); + assertFalse("List should not contain the transient provider", providers.contains(transientProvider)); + + providers.add(transientProvider); + assertTrue("List should contain the transient provider", providers.contains(transientProvider)); + + // another transient provider with same name should be considered equal + Network.Provider transientProviderNew = Network.Provider.createTransientProvider("NetworkExtension"); + assertTrue("List should contain the new transient provider with same name", providers.contains(transientProviderNew)); + } + + @Test + public void testCustomActionServiceLookup() { + Network.Service customAction = Network.Service.getService("CustomAction"); + assertNotNull("CustomAction service should be available", customAction); + assertTrue("CustomAction should be part of the supported services list", + Network.Service.listAllServices().contains(customAction)); + } + + @Test + public void testTransientProviderIsNotGloballyRegistered() { + Network.Provider transientProvider = Network.Provider.createTransientProvider("TransientOnly"); + assertNotNull(transientProvider); + assertNull("Transient provider should not be retrievable from the global registry", + Network.Provider.getProvider("TransientOnly")); + } +} diff --git a/api/src/test/java/com/cloud/network/NetworksTest.java b/api/src/test/java/com/cloud/network/NetworksTest.java index ef5829243421..6f0f3fbd1efe 100644 --- a/api/src/test/java/com/cloud/network/NetworksTest.java +++ b/api/src/test/java/com/cloud/network/NetworksTest.java @@ -37,6 +37,24 @@ public class NetworksTest { public void setUp() { } + @Test + public void nullBroadcastDomainTypeTest() throws URISyntaxException { + BroadcastDomainType type = BroadcastDomainType.getTypeOf(null); + Assert.assertEquals("a null uri should mean a broadcasttype of undecided", BroadcastDomainType.UnDecided, type); + } + + @Test + public void nullBroadcastDomainTypeValueTest() { + URI uri = null; + Assert.assertNull(BroadcastDomainType.getValue(uri)); + } + + @Test + public void nullBroadcastDomainTypeStringValueTest() throws URISyntaxException { + String uriString = null; + Assert.assertNull(BroadcastDomainType.getValue(uriString)); + } + @Test public void emptyBroadcastDomainTypeTest() throws URISyntaxException { BroadcastDomainType type = BroadcastDomainType.getTypeOf(""); diff --git a/api/src/test/java/org/apache/cloudstack/acl/RuleTest.java b/api/src/test/java/org/apache/cloudstack/acl/RuleTest.java index 79e6127d29ad..b99ba48c66dc 100644 --- a/api/src/test/java/org/apache/cloudstack/acl/RuleTest.java +++ b/api/src/test/java/org/apache/cloudstack/acl/RuleTest.java @@ -17,13 +17,46 @@ package org.apache.cloudstack.acl; import com.cloud.exception.InvalidParameterValueException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; +import org.apache.cloudstack.api.APICommand; import org.junit.Assert; +import org.junit.BeforeClass; import org.junit.Test; import java.util.Arrays; +import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.core.type.filter.AnnotationTypeFilter; public class RuleTest { + private static List apiNames; + private static List apiRules; + private static ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false); + + @BeforeClass + public static void setup() { + provider.addIncludeFilter(new AnnotationTypeFilter(APICommand.class)); + Set beanDefinitions = provider.findCandidateComponents("org.apache.cloudstack.api"); + + apiNames = new ArrayList<>(); + apiRules = new ArrayList<>(); + for(BeanDefinition bd : beanDefinitions) { + if (bd instanceof AnnotatedBeanDefinition) { + Map annotationAttributeMap = ((AnnotatedBeanDefinition) bd).getMetadata() + .getAnnotationAttributes(APICommand.class.getName()); + String apiName = annotationAttributeMap.get("name").toString(); + apiNames.add(apiName); + apiRules.add(new Rule(apiName)); + } + } + } + @Test public void testToString() throws Exception { Rule rule = new Rule("someString"); @@ -31,21 +64,89 @@ public void testToString() throws Exception { } @Test - public void testMatchesEmpty() throws Exception { - Rule rule = new Rule("someString"); - Assert.assertFalse(rule.matches("")); + public void ruleMatchesTestNoMatchesOnEmptyString() throws Exception { + String testCmd = ""; + List matches = new ArrayList<>(); + for (Rule rule : apiRules) { + if (rule.matches(testCmd)) { + matches.add(rule.getRuleString()); + } + } + + Assert.assertEquals(matches.size(), 0); } @Test - public void testMatchesNull() throws Exception { - Rule rule = new Rule("someString"); - Assert.assertFalse(rule.matches(null)); + public void ruleMatchesTestNoMatchesOnNull() throws Exception { + List matches = new ArrayList<>(); + for (Rule rule : apiRules) { + if (rule.matches(null)) { + matches.add(rule.getRuleString()); + } + } + + Assert.assertTrue(matches.isEmpty()); } @Test - public void testMatchesSpace() throws Exception { - Rule rule = new Rule("someString"); - Assert.assertFalse(rule.matches(" ")); + public void ruleMatchesTestNoMatchesOnSpaceCharacter() throws Exception { + String testCmd = " "; + List matches = new ArrayList<>(); + for (Rule rule : apiRules) { + if (rule.matches(testCmd)) { + matches.add(rule.getRuleString()); + } + } + + Assert.assertTrue(matches.isEmpty()); + } + + @Test + public void ruleMatchesTestWildCardOnEndWorksAsNormalRegex() { + setup(); + Pattern regexPattern = Pattern.compile("list.*"); + Rule acsRegexRule = new Rule("list*"); + + List nonMatches = new ArrayList<>(); + for (String apiName : apiNames) { + if (acsRegexRule.matches(apiName) != regexPattern.matcher(apiName).matches()) { + nonMatches.add(apiName); + } + } + + Assert.assertTrue(nonMatches.isEmpty()); + } + + @Test + public void ruleMatchesTestWildCardOnMiddleWorksAsNormalRegex() { + setup(); + Pattern regexPattern = Pattern.compile("list.*s"); + Rule acsRegexRule = new Rule("list*s"); + + List nonMatches = new ArrayList<>(); + for (String apiName : apiNames) { + if (acsRegexRule.matches(apiName) != regexPattern.matcher(apiName).matches()) { + nonMatches.add(apiName); + } + } + + Assert.assertTrue(nonMatches.isEmpty()); + } + + @Test + public void ruleMatchesTestWildCardOnStartWorksAsNormalRegex() { + setup(); + Pattern regexPattern = Pattern.compile(".*User"); + Rule acsRegexRule = new Rule("*User"); + + List nonMatches = new ArrayList<>(); + for (String apiName : apiNames) { + if (acsRegexRule.matches(apiName) != regexPattern.matcher(apiName).matches()) { + nonMatches.add(apiName); + } + } + + Assert.assertTrue(nonMatches.isEmpty()); } @Test @@ -73,7 +174,25 @@ public void testMatchesWildcardMiddle() throws Exception { } @Test - public void testValidateRuleWithValidData() throws Exception { + public void ruleMatchesTestWildcardOnRuleAndCommand() throws Exception { + Rule rule = new Rule("*"); + Assert.assertTrue(rule.matches("list*")); + } + + @Test + public void ruleMatchesTestWildcardOnRuleAndCommandNotAllowed() throws Exception { + Rule rule = new Rule("list*"); + Assert.assertFalse(rule.matches("*")); + } + + @Test + public void ruleMatchesTestWithMultipleStars() throws Exception { + Rule rule = new Rule("list***"); + Assert.assertFalse(rule.matches("api")); + } + + @Test + public void testRuleToStringWithValidStrings() throws Exception { for (String rule : Arrays.asList("a", "1", "someApi", "someApi321", "123SomeApi", "prefix*", "*middle*", "*Suffix", "*", "**", "f***", "m0nk3yMa**g1c*")) { @@ -82,7 +201,7 @@ public void testValidateRuleWithValidData() throws Exception { } @Test - public void testValidateRuleWithInvalidData() throws Exception { + public void testRuleToStringWithInvalidStrings() throws Exception { for (String rule : Arrays.asList(null, "", " ", " ", "\n", "\t", "\r", "\"", "\'", "^someApi$", "^someApi", "some$", "some-Api;", "some,Api", "^", "$", "^$", ".*", "\\w+", "r**l3rd0@Kr3", "j@s1n|+|0ȷ", diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/backup/CloneBackupOfferingCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/backup/CloneBackupOfferingCmdTest.java new file mode 100644 index 000000000000..a1412d5a76a7 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/backup/CloneBackupOfferingCmdTest.java @@ -0,0 +1,301 @@ +// 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.backup; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.BackupOfferingResponse; +import org.apache.cloudstack.backup.BackupManager; +import org.apache.cloudstack.backup.BackupOffering; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class CloneBackupOfferingCmdTest { + + private CloneBackupOfferingCmd cloneBackupOfferingCmd; + + @Mock + private BackupManager backupManager; + + @Mock + private ResponseGenerator responseGenerator; + + @Mock + private BackupOffering mockBackupOffering; + + @Mock + private BackupOfferingResponse mockBackupOfferingResponse; + + @Before + public void setUp() { + cloneBackupOfferingCmd = new CloneBackupOfferingCmd(); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "backupManager", backupManager); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "_responseGenerator", responseGenerator); + } + + @Test + public void testGetSourceOfferingId() { + Long sourceOfferingId = 999L; + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "sourceOfferingId", sourceOfferingId); + assertEquals(sourceOfferingId, cloneBackupOfferingCmd.getSourceOfferingId()); + } + + @Test + public void testGetName() { + String name = "ClonedBackupOffering"; + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "name", name); + assertEquals(name, cloneBackupOfferingCmd.getName()); + } + + @Test + public void testGetDescription() { + String description = "Cloned Backup Offering Description"; + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "description", description); + assertEquals(description, cloneBackupOfferingCmd.getDescription()); + } + + @Test + public void testGetZoneId() { + Long zoneId = 123L; + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "zoneId", zoneId); + assertEquals(zoneId, cloneBackupOfferingCmd.getZoneId()); + } + + @Test + public void testGetExternalId() { + String externalId = "external-backup-123"; + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "externalId", externalId); + assertEquals(externalId, cloneBackupOfferingCmd.getExternalId()); + } + + @Test + public void testGetAllowUserDrivenBackups() { + Boolean allowUserDrivenBackups = true; + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "userDrivenBackups", allowUserDrivenBackups); + assertEquals(allowUserDrivenBackups, cloneBackupOfferingCmd.getUserDrivenBackups()); + } + + @Test + public void testAllowUserDrivenBackupsDefaultTrue() { + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "userDrivenBackups", null); + Boolean result = cloneBackupOfferingCmd.getUserDrivenBackups(); + assertTrue(result == null || result); + } + + @Test + public void testAllowUserDrivenBackupsFalse() { + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "userDrivenBackups", false); + assertEquals(Boolean.FALSE, cloneBackupOfferingCmd.getUserDrivenBackups()); + } + + @Test + public void testExecuteSuccess() throws Exception { + Long sourceOfferingId = 999L; + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "sourceOfferingId", sourceOfferingId); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "name", "ClonedBackupOffering"); + + when(backupManager.cloneBackupOffering(any(CloneBackupOfferingCmd.class))).thenReturn(mockBackupOffering); + when(responseGenerator.createBackupOfferingResponse(mockBackupOffering)).thenReturn(mockBackupOfferingResponse); + + cloneBackupOfferingCmd.execute(); + + assertNotNull(cloneBackupOfferingCmd.getResponseObject()); + assertEquals(mockBackupOfferingResponse, cloneBackupOfferingCmd.getResponseObject()); + } + + @Test + public void testExecuteFailure() throws Exception { + Long sourceOfferingId = 999L; + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "sourceOfferingId", sourceOfferingId); + + when(backupManager.cloneBackupOffering(any(CloneBackupOfferingCmd.class))).thenReturn(null); + + try { + cloneBackupOfferingCmd.execute(); + fail("Expected ServerApiException to be thrown"); + } catch (ServerApiException e) { + assertEquals(ApiErrorCode.INTERNAL_ERROR, e.getErrorCode()); + assertEquals("Failed to clone backup offering", e.getMessage()); + } + } + + @Test + public void testExecuteWithInvalidParameterException() throws Exception { + Long sourceOfferingId = 999L; + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "sourceOfferingId", sourceOfferingId); + + when(backupManager.cloneBackupOffering(any(CloneBackupOfferingCmd.class))) + .thenThrow(new InvalidParameterValueException("Invalid source offering ID")); + + try { + cloneBackupOfferingCmd.execute(); + fail("Expected ServerApiException to be thrown"); + } catch (ServerApiException e) { + assertEquals(ApiErrorCode.PARAM_ERROR, e.getErrorCode()); + assertEquals("Invalid source offering ID", e.getMessage()); + } + } + + @Test + public void testExecuteWithCloudRuntimeException() throws Exception { + Long sourceOfferingId = 999L; + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "sourceOfferingId", sourceOfferingId); + + when(backupManager.cloneBackupOffering(any(CloneBackupOfferingCmd.class))) + .thenThrow(new CloudRuntimeException("Runtime error during clone")); + + try { + cloneBackupOfferingCmd.execute(); + fail("Expected ServerApiException to be thrown"); + } catch (ServerApiException e) { + assertEquals(ApiErrorCode.INTERNAL_ERROR, e.getErrorCode()); + assertEquals("Runtime error during clone", e.getMessage()); + } + } + + @Test + public void testExecuteSuccessWithAllParameters() throws Exception { + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "sourceOfferingId", 999L); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "name", "ClonedBackupOffering"); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "description", "Test Description"); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "zoneId", 123L); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "externalId", "ext-123"); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "userDrivenBackups", true); + + when(backupManager.cloneBackupOffering(any(CloneBackupOfferingCmd.class))).thenReturn(mockBackupOffering); + when(responseGenerator.createBackupOfferingResponse(mockBackupOffering)).thenReturn(mockBackupOfferingResponse); + + cloneBackupOfferingCmd.execute(); + + assertNotNull(cloneBackupOfferingCmd.getResponseObject()); + assertEquals(mockBackupOfferingResponse, cloneBackupOfferingCmd.getResponseObject()); + } + + @Test + public void testCloneWithAllParameters() { + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "sourceOfferingId", 999L); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "name", "ClonedBackupOffering"); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "description", "Cloned backup offering for testing"); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "zoneId", 123L); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "externalId", "external-backup-123"); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "userDrivenBackups", true); + + assertEquals(Long.valueOf(999L), cloneBackupOfferingCmd.getSourceOfferingId()); + assertEquals("ClonedBackupOffering", cloneBackupOfferingCmd.getName()); + assertEquals("Cloned backup offering for testing", cloneBackupOfferingCmd.getDescription()); + assertEquals(Long.valueOf(123L), cloneBackupOfferingCmd.getZoneId()); + assertEquals("external-backup-123", cloneBackupOfferingCmd.getExternalId()); + assertEquals(Boolean.TRUE, cloneBackupOfferingCmd.getUserDrivenBackups()); + } + + @Test + public void testCloneWithMinimalParameters() { + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "sourceOfferingId", 999L); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "name", "ClonedBackupOffering"); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "description", "Description"); + + assertEquals(Long.valueOf(999L), cloneBackupOfferingCmd.getSourceOfferingId()); + assertEquals("ClonedBackupOffering", cloneBackupOfferingCmd.getName()); + assertEquals("Description", cloneBackupOfferingCmd.getDescription()); + + assertNull(cloneBackupOfferingCmd.getZoneId()); + assertNull(cloneBackupOfferingCmd.getExternalId()); + } + + @Test + public void testSourceOfferingIdNullByDefault() { + assertNull(cloneBackupOfferingCmd.getSourceOfferingId()); + } + + @Test + public void testNameNullByDefault() { + assertNull(cloneBackupOfferingCmd.getName()); + } + + @Test + public void testDescriptionNullByDefault() { + assertNull(cloneBackupOfferingCmd.getDescription()); + } + + @Test + public void testZoneIdNullByDefault() { + assertNull(cloneBackupOfferingCmd.getZoneId()); + } + + @Test + public void testExternalIdNullByDefault() { + assertNull(cloneBackupOfferingCmd.getExternalId()); + } + + @Test + public void testCloneBackupOfferingInheritingZone() { + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "sourceOfferingId", 999L); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "name", "ClonedBackupOffering"); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "description", "Clone with inherited zone"); + + assertEquals(Long.valueOf(999L), cloneBackupOfferingCmd.getSourceOfferingId()); + assertNull(cloneBackupOfferingCmd.getZoneId()); + } + + @Test + public void testCloneBackupOfferingInheritingExternalId() { + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "sourceOfferingId", 999L); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "name", "ClonedBackupOffering"); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "description", "Clone with inherited external ID"); + + assertEquals(Long.valueOf(999L), cloneBackupOfferingCmd.getSourceOfferingId()); + assertNull(cloneBackupOfferingCmd.getExternalId()); + } + + @Test + public void testCloneBackupOfferingOverridingZone() { + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "sourceOfferingId", 999L); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "name", "ClonedBackupOffering"); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "description", "Clone with new zone"); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "zoneId", 456L); + + assertEquals(Long.valueOf(999L), cloneBackupOfferingCmd.getSourceOfferingId()); + assertEquals(Long.valueOf(456L), cloneBackupOfferingCmd.getZoneId()); + } + + @Test + public void testCloneBackupOfferingDisallowUserDrivenBackups() { + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "sourceOfferingId", 999L); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "name", "ClonedBackupOffering"); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "description", "Clone without user-driven backups"); + ReflectionTestUtils.setField(cloneBackupOfferingCmd, "userDrivenBackups", false); + + assertEquals(Boolean.FALSE, cloneBackupOfferingCmd.getUserDrivenBackups()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/config/UpdateCfgCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/config/UpdateCfgCmdTest.java new file mode 100644 index 000000000000..51b1cd9e14b7 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/config/UpdateCfgCmdTest.java @@ -0,0 +1,81 @@ +// 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.config; + +import org.apache.cloudstack.api.response.ConfigurationResponse; +import org.apache.cloudstack.config.Configuration; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import com.cloud.utils.crypt.DBEncryptionUtil; + +@RunWith(MockitoJUnitRunner.class) +public class UpdateCfgCmdTest { + + private UpdateCfgCmd updateCfgCmd; + + private MockedStatic mockedStatic; + + @Before + public void setUp() { + updateCfgCmd = new UpdateCfgCmd(); + mockedStatic = Mockito.mockStatic(DBEncryptionUtil.class); + } + + @After + public void tearDown() { + mockedStatic.close(); + } + + @Test + public void setResponseValueSetsEncryptedValueWhenConfigurationIsEncrypted() { + ConfigurationResponse response = new ConfigurationResponse(); + Configuration cfg = Mockito.mock(Configuration.class); + Mockito.when(cfg.isEncrypted()).thenReturn(true); + Mockito.when(cfg.getValue()).thenReturn("testValue"); + Mockito.when(DBEncryptionUtil.encrypt("testValue")).thenReturn("encryptedValue"); + updateCfgCmd.setResponseValue(response, cfg); + Assert.assertEquals("encryptedValue", response.getValue()); + } + + @Test + public void setResponseValueSetsPlainValueWhenConfigurationIsNotEncrypted() { + ConfigurationResponse response = new ConfigurationResponse(); + Configuration cfg = Mockito.mock(Configuration.class); + Mockito.when(cfg.isEncrypted()).thenReturn(false); + Mockito.when(cfg.getValue()).thenReturn("testValue"); + updateCfgCmd.setResponseValue(response, cfg); + Assert.assertEquals("testValue", response.getValue()); + } + + @Test + public void setResponseValueHandlesNullConfigurationValueGracefully() { + ConfigurationResponse response = new ConfigurationResponse(); + Configuration cfg = Mockito.mock(Configuration.class); + Mockito.when(cfg.isEncrypted()).thenReturn(false); + Mockito.when(cfg.getValue()).thenReturn(null); + updateCfgCmd.setResponseValue(response, cfg); + Assert.assertNull(response.getValue()); + } + +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CloneNetworkOfferingCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CloneNetworkOfferingCmdTest.java new file mode 100644 index 000000000000..096395b1359e --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CloneNetworkOfferingCmdTest.java @@ -0,0 +1,324 @@ +// 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.network; + +import com.cloud.offering.NetworkOffering; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.NetworkOfferingResponse; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Arrays; +import java.util.HashMap; +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.assertNull; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class CloneNetworkOfferingCmdTest { + + private CloneNetworkOfferingCmd cloneNetworkOfferingCmd; + + @Mock + private com.cloud.configuration.ConfigurationService configService; + + @Mock + private ResponseGenerator responseGenerator; + + @Mock + private NetworkOffering mockNetworkOffering; + + @Mock + private NetworkOfferingResponse mockNetworkOfferingResponse; + + @Before + public void setUp() { + cloneNetworkOfferingCmd = new CloneNetworkOfferingCmd(); + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "_configService", configService); + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "_responseGenerator", responseGenerator); + } + + @Test + public void testGetSourceOfferingId() { + Long sourceOfferingId = 123L; + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "sourceOfferingId", sourceOfferingId); + assertEquals(sourceOfferingId, cloneNetworkOfferingCmd.getSourceOfferingId()); + } + + @Test + public void testGetAddServices() { + List addServices = Arrays.asList("Dhcp", "Dns"); + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "addServices", addServices); + assertEquals(addServices, cloneNetworkOfferingCmd.getAddServices()); + } + + @Test + public void testGetDropServices() { + List dropServices = Arrays.asList("Firewall", "Vpn"); + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "dropServices", dropServices); + assertEquals(dropServices, cloneNetworkOfferingCmd.getDropServices()); + } + + @Test + public void testGetGuestIpType() { + String guestIpType = "Isolated"; + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "guestIptype", guestIpType); + assertEquals(guestIpType, cloneNetworkOfferingCmd.getGuestIpType()); + } + + @Test + public void testGetTraffictype() { + String trafficType = "GUEST"; + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "traffictype", trafficType); + assertEquals(trafficType, cloneNetworkOfferingCmd.getTraffictype()); + } + + @Test + public void testGetName() { + String name = "ClonedNetworkOffering"; + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "networkOfferingName", name); + assertEquals(name, cloneNetworkOfferingCmd.getNetworkOfferingName()); + } + + @Test + public void testGetDisplayText() { + String displayText = "Cloned Network Offering Display Text"; + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "displayText", displayText); + assertEquals(displayText, cloneNetworkOfferingCmd.getDisplayText()); + } + + @Test + public void testGetDisplayTextDefaultsToName() { + String name = "ClonedNetworkOffering"; + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "networkOfferingName", name); + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "displayText", null); + assertEquals(name, cloneNetworkOfferingCmd.getDisplayText()); + } + + @Test + public void testGetAvailability() { + String availability = "Required"; + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "availability", availability); + assertEquals(availability, cloneNetworkOfferingCmd.getAvailability()); + } + + @Test + public void testGetTags() { + String tags = "tag1,tag2,tag3"; + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "tags", tags); + assertEquals(tags, cloneNetworkOfferingCmd.getTags()); + } + + @Test + public void testExecuteSuccess() { + Long sourceOfferingId = 123L; + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "sourceOfferingId", sourceOfferingId); + + when(configService.cloneNetworkOffering(any(CloneNetworkOfferingCmd.class))).thenReturn(mockNetworkOffering); + when(responseGenerator.createNetworkOfferingResponse(mockNetworkOffering)).thenReturn(mockNetworkOfferingResponse); + + cloneNetworkOfferingCmd.execute(); + + assertNotNull(cloneNetworkOfferingCmd.getResponseObject()); + assertEquals(mockNetworkOfferingResponse, cloneNetworkOfferingCmd.getResponseObject()); + } + + @Test(expected = ServerApiException.class) + public void testExecuteFailure() { + Long sourceOfferingId = 123L; + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "sourceOfferingId", sourceOfferingId); + + when(configService.cloneNetworkOffering(any(CloneNetworkOfferingCmd.class))).thenReturn(null); + + try { + cloneNetworkOfferingCmd.execute(); + fail("Expected ServerApiException to be thrown"); + } catch (ServerApiException e) { + assertEquals(ApiErrorCode.INTERNAL_ERROR, e.getErrorCode()); + assertEquals("Failed to clone network offering", e.getMessage()); + throw e; + } + } + + @Test + public void testGetConserveMode() { + Boolean conserveMode = true; + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "conserveMode", conserveMode); + assertEquals(conserveMode, cloneNetworkOfferingCmd.getConserveMode()); + } + + @Test + public void testGetSpecifyVlan() { + Boolean specifyVlan = false; + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "specifyVlan", specifyVlan); + assertEquals(specifyVlan, cloneNetworkOfferingCmd.getSpecifyVlan()); + } + + @Test + public void testGetSpecifyIpRanges() { + Boolean specifyIpRanges = true; + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "specifyIpRanges", specifyIpRanges); + assertEquals(specifyIpRanges, cloneNetworkOfferingCmd.getSpecifyIpRanges()); + } + + @Test + public void testGetIsPersistent() { + Boolean isPersistent = true; + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "isPersistent", isPersistent); + assertEquals(isPersistent, cloneNetworkOfferingCmd.getIsPersistent()); + } + + @Test + public void testGetEgressDefaultPolicy() { + Boolean egressDefaultPolicy = false; + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "egressDefaultPolicy", egressDefaultPolicy); + assertEquals(egressDefaultPolicy, cloneNetworkOfferingCmd.getEgressDefaultPolicy()); + } + + @Test + public void testGetServiceOfferingId() { + Long serviceOfferingId = 456L; + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "serviceOfferingId", serviceOfferingId); + assertEquals(serviceOfferingId, cloneNetworkOfferingCmd.getServiceOfferingId()); + } + + @Test + public void testGetForVpc() { + Boolean forVpc = true; + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "forVpc", forVpc); + assertEquals(forVpc, cloneNetworkOfferingCmd.getForVpc()); + } + + @Test + public void testGetMaxConnections() { + Integer maxConnections = 1000; + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "maxConnections", maxConnections); + assertEquals(maxConnections, cloneNetworkOfferingCmd.getMaxconnections()); + } + + @Test + public void testGetNetworkRate() { + Integer networkRate = 200; + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "networkRate", networkRate); + assertEquals(networkRate, cloneNetworkOfferingCmd.getNetworkRate()); + } + + @Test + public void testGetInternetProtocol() { + String internetProtocol = "ipv4"; + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "internetProtocol", internetProtocol); + assertEquals(internetProtocol, cloneNetworkOfferingCmd.getInternetProtocol()); + } + + @Test + public void testAddServicesNullByDefault() { + assertNull(cloneNetworkOfferingCmd.getAddServices()); + } + + @Test + public void testDropServicesNullByDefault() { + assertNull(cloneNetworkOfferingCmd.getDropServices()); + } + + @Test + public void testSupportedServicesParameter() { + List supportedServices = Arrays.asList("Dhcp", "Dns", "SourceNat"); + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "supportedServices", supportedServices); + assertEquals(supportedServices, cloneNetworkOfferingCmd.getSupportedServices()); + } + + @Test + public void testServiceProviderListParameter() { + Map> serviceProviderList = new HashMap<>(); + + HashMap dhcpProvider = new HashMap<>(); + dhcpProvider.put("service", "Dhcp"); + dhcpProvider.put("provider", "VirtualRouter"); + + HashMap dnsProvider = new HashMap<>(); + dnsProvider.put("service", "Dns"); + dnsProvider.put("provider", "VirtualRouter"); + + serviceProviderList.put("0", dhcpProvider); + serviceProviderList.put("1", dnsProvider); + + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "serviceProviderList", serviceProviderList); + + Map> result = cloneNetworkOfferingCmd.getServiceProviders(); + assertNotNull(result); + assertEquals(2, result.size()); + assertNotNull(result.get("Dhcp")); + assertNotNull(result.get("Dns")); + assertEquals("VirtualRouter", result.get("Dhcp").get(0)); + assertEquals("VirtualRouter", result.get("Dns").get(0)); + } + + @Test + public void testCloneWithAllParameters() { + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "sourceOfferingId", 123L); + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "networkOfferingName", "ClonedOffering"); + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "displayText", "Cloned Offering Display"); + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "availability", "Optional"); + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "guestIptype", "Isolated"); + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "traffictype", "GUEST"); + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "conserveMode", true); + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "specifyVlan", false); + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "isPersistent", true); + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "egressDefaultPolicy", false); + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "networkRate", 200); + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "serviceOfferingId", 456L); + + assertEquals(Long.valueOf(123L), cloneNetworkOfferingCmd.getSourceOfferingId()); + assertEquals("ClonedOffering", cloneNetworkOfferingCmd.getNetworkOfferingName()); + assertEquals("Cloned Offering Display", cloneNetworkOfferingCmd.getDisplayText()); + assertEquals("Optional", cloneNetworkOfferingCmd.getAvailability()); + assertEquals("Isolated", cloneNetworkOfferingCmd.getGuestIpType()); + assertEquals("GUEST", cloneNetworkOfferingCmd.getTraffictype()); + assertEquals(Boolean.TRUE, cloneNetworkOfferingCmd.getConserveMode()); + assertEquals(Boolean.FALSE, cloneNetworkOfferingCmd.getSpecifyVlan()); + assertEquals(Boolean.TRUE, cloneNetworkOfferingCmd.getIsPersistent()); + assertEquals(Boolean.FALSE, cloneNetworkOfferingCmd.getEgressDefaultPolicy()); + assertEquals(Integer.valueOf(200), cloneNetworkOfferingCmd.getNetworkRate()); + assertEquals(Long.valueOf(456L), cloneNetworkOfferingCmd.getServiceOfferingId()); + } + + @Test + public void testCloneWithAddAndDropServices() { + List addServices = Arrays.asList("StaticNat", "PortForwarding"); + List dropServices = Arrays.asList("Vpn"); + + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "sourceOfferingId", 123L); + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "addServices", addServices); + ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "dropServices", dropServices); + + assertEquals(addServices, cloneNetworkOfferingCmd.getAddServices()); + assertEquals(dropServices, cloneNetworkOfferingCmd.getDropServices()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmdTest.java index e1393e316993..38d0df2e8b82 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmdTest.java @@ -20,6 +20,7 @@ import com.cloud.event.EventTypes; import org.apache.cloudstack.api.response.Ipv4SubnetForGuestNetworkResponse; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap; import org.apache.cloudstack.network.RoutedIpv4Manager; import org.junit.Assert; @@ -29,6 +30,8 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; +import java.util.UUID; + @RunWith(MockitoJUnitRunner.class) public class CreateIpv4SubnetForGuestNetworkCmdTest { @@ -37,6 +40,7 @@ public class CreateIpv4SubnetForGuestNetworkCmdTest { @Test public void testCreateIpv4SubnetForGuestNetworkCmd() { Long parentId = 1L; + String parentUuid = UUID.randomUUID().toString(); String subnet = "192.168.1.0/24"; Integer cidrSize = 26; @@ -46,12 +50,14 @@ public void testCreateIpv4SubnetForGuestNetworkCmd() { ReflectionTestUtils.setField(cmd, "cidrSize", cidrSize); ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + CallContext.current().putApiResourceUuid("parentid", parentUuid); + Assert.assertEquals(parentId, cmd.getParentId()); Assert.assertEquals(subnet, cmd.getSubnet()); Assert.assertEquals(cidrSize, cmd.getCidrSize()); Assert.assertEquals(1L, cmd.getEntityOwnerId()); Assert.assertEquals(EventTypes.EVENT_IP4_GUEST_SUBNET_CREATE, cmd.getEventType()); - Assert.assertEquals(String.format("Creating guest IPv4 subnet %s in zone subnet=%s", subnet, parentId), cmd.getEventDescription()); + Assert.assertEquals(String.format("Creating guest IPv4 subnet %s in zone subnet: %s", subnet, parentUuid), cmd.getEventDescription()); Ipv4GuestSubnetNetworkMap ipv4GuestSubnetNetworkMap = Mockito.mock(Ipv4GuestSubnetNetworkMap.class); Mockito.when(routedIpv4Manager.createIpv4SubnetForGuestNetwork(cmd)).thenReturn(ipv4GuestSubnetNetworkMap); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForZoneCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForZoneCmdTest.java index 51c1eb986c47..560ecdc3b296 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForZoneCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForZoneCmdTest.java @@ -20,6 +20,7 @@ import com.cloud.event.EventTypes; import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; import org.apache.cloudstack.network.RoutedIpv4Manager; import org.junit.Assert; @@ -29,6 +30,8 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; +import java.util.UUID; + @RunWith(MockitoJUnitRunner.class) public class CreateIpv4SubnetForZoneCmdTest { @@ -37,6 +40,7 @@ public class CreateIpv4SubnetForZoneCmdTest { @Test public void testCreateIpv4SubnetForZoneCmd() { Long zoneId = 1L; + String zoneUuid = UUID.randomUUID().toString(); String subnet = "192.168.1.0/24"; String accountName = "user"; Long projectId = 10L; @@ -50,6 +54,8 @@ public void testCreateIpv4SubnetForZoneCmd() { ReflectionTestUtils.setField(cmd,"domainId", domainId); ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + CallContext.current().putApiResourceUuid("zoneid", zoneUuid); + Assert.assertEquals(zoneId, cmd.getZoneId()); Assert.assertEquals(subnet, cmd.getSubnet()); Assert.assertEquals(accountName, cmd.getAccountName()); @@ -57,7 +63,7 @@ public void testCreateIpv4SubnetForZoneCmd() { Assert.assertEquals(domainId, cmd.getDomainId()); Assert.assertEquals(1L, cmd.getEntityOwnerId()); Assert.assertEquals(EventTypes.EVENT_ZONE_IP4_SUBNET_CREATE, cmd.getEventType()); - Assert.assertEquals(String.format("Creating guest IPv4 subnet %s for zone=%s", subnet, zoneId), cmd.getEventDescription()); + Assert.assertEquals(String.format("Creating guest IPv4 subnet %s for zone: %s", subnet, zoneUuid), cmd.getEventDescription()); DataCenterIpv4GuestSubnet zoneSubnet = Mockito.mock(DataCenterIpv4GuestSubnet.class); Mockito.when(routedIpv4Manager.createDataCenterIpv4GuestSubnet(cmd)).thenReturn(zoneSubnet); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DedicateIpv4SubnetForZoneCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DedicateIpv4SubnetForZoneCmdTest.java index 7db77098b233..4640510ccdab 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DedicateIpv4SubnetForZoneCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DedicateIpv4SubnetForZoneCmdTest.java @@ -19,6 +19,7 @@ import com.cloud.event.EventTypes; import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; import org.apache.cloudstack.network.RoutedIpv4Manager; import org.junit.Assert; @@ -28,6 +29,8 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; +import java.util.UUID; + @RunWith(MockitoJUnitRunner.class) public class DedicateIpv4SubnetForZoneCmdTest { @@ -36,6 +39,7 @@ public class DedicateIpv4SubnetForZoneCmdTest { @Test public void testDedicateIpv4SubnetForZoneCmd() { Long id = 1L; + String uuid = UUID.randomUUID().toString(); String accountName = "user"; Long projectId = 10L; Long domainId = 11L; @@ -47,6 +51,8 @@ public void testDedicateIpv4SubnetForZoneCmd() { ReflectionTestUtils.setField(cmd,"domainId", domainId); ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + CallContext.current().putApiResourceUuid("id", uuid); + Assert.assertEquals(id, cmd.getId()); Assert.assertEquals(accountName, cmd.getAccountName()); Assert.assertEquals(projectId, cmd.getProjectId()); @@ -54,7 +60,7 @@ public void testDedicateIpv4SubnetForZoneCmd() { Assert.assertEquals(1L, cmd.getEntityOwnerId()); Assert.assertEquals(EventTypes.EVENT_ZONE_IP4_SUBNET_DEDICATE, cmd.getEventType()); - Assert.assertEquals(String.format("Dedicating zone IPv4 subnet %s", id), cmd.getEventDescription()); + Assert.assertEquals(String.format("Dedicating zone's IPv4 subnet with ID: %s", uuid), cmd.getEventDescription()); DataCenterIpv4GuestSubnet zoneSubnet = Mockito.mock(DataCenterIpv4GuestSubnet.class); Mockito.when(routedIpv4Manager.dedicateDataCenterIpv4GuestSubnet(cmd)).thenReturn(zoneSubnet); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForGuestNetworkCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForGuestNetworkCmdTest.java index a4af5ddf748f..cd25d8d24014 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForGuestNetworkCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForGuestNetworkCmdTest.java @@ -20,6 +20,7 @@ import com.cloud.event.EventTypes; import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.network.RoutedIpv4Manager; import org.junit.Assert; import org.junit.Test; @@ -28,6 +29,8 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; +import java.util.UUID; + @RunWith(MockitoJUnitRunner.class) public class DeleteIpv4SubnetForGuestNetworkCmdTest { @@ -36,15 +39,18 @@ public class DeleteIpv4SubnetForGuestNetworkCmdTest { @Test public void testDeleteIpv4SubnetForGuestNetworkCmd() { Long id = 1L; + String uuid = UUID.randomUUID().toString(); DeleteIpv4SubnetForGuestNetworkCmd cmd = new DeleteIpv4SubnetForGuestNetworkCmd(); ReflectionTestUtils.setField(cmd, "id", id); ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + CallContext.current().putApiResourceUuid("id", uuid); + Assert.assertEquals(id, cmd.getId()); Assert.assertEquals(1L, cmd.getEntityOwnerId()); Assert.assertEquals(EventTypes.EVENT_IP4_GUEST_SUBNET_DELETE, cmd.getEventType()); - Assert.assertEquals(String.format("Deleting guest IPv4 subnet %s", id), cmd.getEventDescription()); + Assert.assertEquals(String.format("Deleting guest IPv4 subnet with ID: %s", uuid), cmd.getEventDescription()); Mockito.when(routedIpv4Manager.deleteIpv4SubnetForGuestNetwork(cmd)).thenReturn(true); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForZoneCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForZoneCmdTest.java index 7af173f09d96..269fb3f3c192 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForZoneCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForZoneCmdTest.java @@ -20,6 +20,7 @@ import com.cloud.event.EventTypes; import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.network.RoutedIpv4Manager; import org.junit.Assert; import org.junit.Test; @@ -28,6 +29,8 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; +import java.util.UUID; + @RunWith(MockitoJUnitRunner.class) public class DeleteIpv4SubnetForZoneCmdTest { @@ -36,15 +39,18 @@ public class DeleteIpv4SubnetForZoneCmdTest { @Test public void testDeleteIpv4SubnetForZoneCmd() { Long id = 1L; + String uuid = UUID.randomUUID().toString(); DeleteIpv4SubnetForZoneCmd cmd = new DeleteIpv4SubnetForZoneCmd(); ReflectionTestUtils.setField(cmd, "id", id); ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + CallContext.current().putApiResourceUuid("id", uuid); + Assert.assertEquals(id, cmd.getId()); Assert.assertEquals(1L, cmd.getEntityOwnerId()); Assert.assertEquals(EventTypes.EVENT_ZONE_IP4_SUBNET_DELETE, cmd.getEventType()); - Assert.assertEquals(String.format("Deleting zone IPv4 subnet %s", id), cmd.getEventDescription()); + Assert.assertEquals(String.format("Deleting zone IPv4 subnet with ID: %s", uuid), cmd.getEventDescription()); Mockito.when(routedIpv4Manager.deleteDataCenterIpv4GuestSubnet(cmd)).thenReturn(true); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedIpv4SubnetForZoneCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedIpv4SubnetForZoneCmdTest.java index 9ce9a4f9464f..6e5d2a95f3ab 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedIpv4SubnetForZoneCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedIpv4SubnetForZoneCmdTest.java @@ -19,6 +19,7 @@ import com.cloud.event.EventTypes; import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; import org.apache.cloudstack.network.RoutedIpv4Manager; import org.junit.Assert; @@ -28,6 +29,8 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; +import java.util.UUID; + @RunWith(MockitoJUnitRunner.class) public class ReleaseDedicatedIpv4SubnetForZoneCmdTest { @@ -36,15 +39,18 @@ public class ReleaseDedicatedIpv4SubnetForZoneCmdTest { @Test public void testReleaseDedicatedIpv4SubnetForZoneCmd() { Long id = 1L; + String uuid = UUID.randomUUID().toString(); ReleaseDedicatedIpv4SubnetForZoneCmd cmd = new ReleaseDedicatedIpv4SubnetForZoneCmd(); ReflectionTestUtils.setField(cmd, "id", id); ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + CallContext.current().putApiResourceUuid("id", uuid); + Assert.assertEquals(id, cmd.getId()); Assert.assertEquals(1L, cmd.getEntityOwnerId()); Assert.assertEquals(EventTypes.EVENT_ZONE_IP4_SUBNET_RELEASE, cmd.getEventType()); - Assert.assertEquals(String.format("Releasing a dedicated zone IPv4 subnet %s", id), cmd.getEventDescription()); + Assert.assertEquals(String.format("Releasing dedicated zone IPv4 subnet with ID: %s", uuid), cmd.getEventDescription()); DataCenterIpv4GuestSubnet zoneSubnet = Mockito.mock(DataCenterIpv4GuestSubnet.class); Mockito.when(routedIpv4Manager.releaseDedicatedDataCenterIpv4GuestSubnet(cmd)).thenReturn(zoneSubnet); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/UpdateIpv4SubnetForZoneCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/UpdateIpv4SubnetForZoneCmdTest.java index cdb9cce22d83..af37006eafd2 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/UpdateIpv4SubnetForZoneCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/UpdateIpv4SubnetForZoneCmdTest.java @@ -20,6 +20,7 @@ import com.cloud.event.EventTypes; import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; import org.apache.cloudstack.network.RoutedIpv4Manager; import org.junit.Assert; @@ -29,6 +30,8 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; +import java.util.UUID; + @RunWith(MockitoJUnitRunner.class) public class UpdateIpv4SubnetForZoneCmdTest { @@ -37,6 +40,7 @@ public class UpdateIpv4SubnetForZoneCmdTest { @Test public void testUpdateIpv4SubnetForZoneCmd() { Long id = 1L; + String uuid = UUID.randomUUID().toString(); String subnet = "192.168.1.0/24"; UpdateIpv4SubnetForZoneCmd cmd = new UpdateIpv4SubnetForZoneCmd(); @@ -44,11 +48,13 @@ public void testUpdateIpv4SubnetForZoneCmd() { ReflectionTestUtils.setField(cmd, "subnet", subnet); ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + CallContext.current().putApiResourceUuid("id", uuid); + Assert.assertEquals(id, cmd.getId()); Assert.assertEquals(subnet, cmd.getSubnet()); Assert.assertEquals(1L, cmd.getEntityOwnerId()); Assert.assertEquals(EventTypes.EVENT_ZONE_IP4_SUBNET_UPDATE, cmd.getEventType()); - Assert.assertEquals(String.format("Updating zone IPv4 subnet %s", id), cmd.getEventDescription()); + Assert.assertEquals(String.format("Updating zone IPv4 subnet with ID: %s", uuid), cmd.getEventDescription()); DataCenterIpv4GuestSubnet zoneSubnet = Mockito.mock(DataCenterIpv4GuestSubnet.class); Mockito.when(routedIpv4Manager.updateDataCenterIpv4GuestSubnet(cmd)).thenReturn(zoneSubnet); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForNetworkCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForNetworkCmdTest.java index 28ddad17afe5..3db1fab466f6 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForNetworkCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForNetworkCmdTest.java @@ -23,6 +23,7 @@ import org.apache.cloudstack.api.ResponseGenerator; import org.apache.cloudstack.api.ResponseObject; import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.network.RoutedIpv4Manager; import org.junit.Assert; import org.junit.Test; @@ -33,6 +34,7 @@ import java.util.Arrays; import java.util.List; +import java.util.UUID; @RunWith(MockitoJUnitRunner.class) public class ChangeBgpPeersForNetworkCmdTest { @@ -44,6 +46,7 @@ public class ChangeBgpPeersForNetworkCmdTest { @Test public void testChangeBgpPeersForNetworkCmd() { Long networkId = 10L; + String networkUuid = UUID.randomUUID().toString(); List bgpPeerIds = Arrays.asList(20L, 21L); ChangeBgpPeersForNetworkCmd cmd = new ChangeBgpPeersForNetworkCmd(); @@ -52,11 +55,13 @@ public void testChangeBgpPeersForNetworkCmd() { ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); ReflectionTestUtils.setField(cmd,"_responseGenerator", _responseGenerator); + CallContext.current().putApiResourceUuid("networkid", networkUuid); + Assert.assertEquals(networkId, cmd.getNetworkId()); Assert.assertEquals(bgpPeerIds, cmd.getBgpPeerIds()); Assert.assertEquals(1L, cmd.getEntityOwnerId()); Assert.assertEquals(EventTypes.EVENT_NETWORK_BGP_PEER_UPDATE, cmd.getEventType()); - Assert.assertEquals(String.format("Changing Bgp Peers for network %s", networkId), cmd.getEventDescription()); + Assert.assertEquals(String.format("Changing BGP Peers for Network with ID: %s", networkUuid), cmd.getEventDescription()); Network network = Mockito.mock(Network.class); Mockito.when(routedIpv4Manager.changeBgpPeersForNetwork(cmd)).thenReturn(network); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForVpcCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForVpcCmdTest.java index 96eb1f020de2..fb85f7060684 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForVpcCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForVpcCmdTest.java @@ -23,6 +23,7 @@ import org.apache.cloudstack.api.ResponseGenerator; import org.apache.cloudstack.api.ResponseObject; import org.apache.cloudstack.api.response.VpcResponse; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.network.RoutedIpv4Manager; import org.junit.Assert; import org.junit.Test; @@ -33,6 +34,7 @@ import java.util.Arrays; import java.util.List; +import java.util.UUID; @RunWith(MockitoJUnitRunner.class) public class ChangeBgpPeersForVpcCmdTest { @@ -44,6 +46,7 @@ public class ChangeBgpPeersForVpcCmdTest { @Test public void testChangeBgpPeersForVpcCmd() { Long VpcId = 10L; + String vpcUuid = UUID.randomUUID().toString(); List bgpPeerIds = Arrays.asList(20L, 21L); ChangeBgpPeersForVpcCmd cmd = new ChangeBgpPeersForVpcCmd(); @@ -52,11 +55,13 @@ public void testChangeBgpPeersForVpcCmd() { ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); ReflectionTestUtils.setField(cmd,"_responseGenerator", _responseGenerator); + CallContext.current().putApiResourceUuid("vpcid", vpcUuid); + Assert.assertEquals(VpcId, cmd.getVpcId()); Assert.assertEquals(bgpPeerIds, cmd.getBgpPeerIds()); Assert.assertEquals(1L, cmd.getEntityOwnerId()); Assert.assertEquals(EventTypes.EVENT_VPC_BGP_PEER_UPDATE, cmd.getEventType()); - Assert.assertEquals(String.format("Changing Bgp Peers for VPC %s", VpcId), cmd.getEventDescription()); + Assert.assertEquals(String.format("Changing BGP Peers for VPC with ID: %s", vpcUuid), cmd.getEventDescription()); Vpc Vpc = Mockito.mock(Vpc.class); Mockito.when(routedIpv4Manager.changeBgpPeersForVpc(cmd)).thenReturn(Vpc); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/CreateBgpPeerCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/CreateBgpPeerCmdTest.java index 0d802bf36199..3abc5f57d017 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/CreateBgpPeerCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/CreateBgpPeerCmdTest.java @@ -20,6 +20,7 @@ import com.cloud.event.EventTypes; import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.network.BgpPeer; import org.apache.cloudstack.network.RoutedIpv4Manager; import org.junit.Assert; @@ -29,6 +30,8 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; +import java.util.UUID; + @RunWith(MockitoJUnitRunner.class) public class CreateBgpPeerCmdTest { @@ -37,6 +40,7 @@ public class CreateBgpPeerCmdTest { @Test public void testCreateBgpPeerCmd() { Long zoneId = 1L; + String zoneUuid = UUID.randomUUID().toString(); String accountName = "user"; Long projectId = 10L; Long domainId = 11L; @@ -56,6 +60,8 @@ public void testCreateBgpPeerCmd() { ReflectionTestUtils.setField(cmd,"domainId", domainId); ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + CallContext.current().putApiResourceUuid("zoneid", zoneUuid); + Assert.assertEquals(zoneId, cmd.getZoneId()); Assert.assertEquals(ip4Address, cmd.getIp4Address()); Assert.assertEquals(ip6Address, cmd.getIp6Address()); @@ -67,7 +73,7 @@ public void testCreateBgpPeerCmd() { Assert.assertEquals(1L, cmd.getEntityOwnerId()); Assert.assertEquals(EventTypes.EVENT_BGP_PEER_CREATE, cmd.getEventType()); - Assert.assertEquals(String.format("Creating Bgp Peer %s for zone=%s", peerAsNumber, zoneId), cmd.getEventDescription()); + Assert.assertEquals(String.format("Creating BGP Peer %s for zone with ID: %s", peerAsNumber, zoneUuid), cmd.getEventDescription()); BgpPeer bgpPeer = Mockito.mock(BgpPeer.class); Mockito.when(routedIpv4Manager.createBgpPeer(cmd)).thenReturn(bgpPeer); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/DedicateBgpPeerCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/DedicateBgpPeerCmdTest.java index f3ae007da285..c5edb1b8f53f 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/DedicateBgpPeerCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/DedicateBgpPeerCmdTest.java @@ -19,6 +19,7 @@ import com.cloud.event.EventTypes; import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.network.BgpPeer; import org.apache.cloudstack.network.RoutedIpv4Manager; import org.junit.Assert; @@ -28,6 +29,8 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; +import java.util.UUID; + @RunWith(MockitoJUnitRunner.class) public class DedicateBgpPeerCmdTest { @@ -36,6 +39,7 @@ public class DedicateBgpPeerCmdTest { @Test public void testDedicateBgpPeerCmd() { Long id = 1L; + String uuid = UUID.randomUUID().toString(); String accountName = "user"; Long projectId = 10L; Long domainId = 11L; @@ -47,6 +51,8 @@ public void testDedicateBgpPeerCmd() { ReflectionTestUtils.setField(cmd,"domainId", domainId); ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + CallContext.current().putApiResourceUuid("id", uuid); + Assert.assertEquals(id, cmd.getId()); Assert.assertEquals(accountName, cmd.getAccountName()); Assert.assertEquals(projectId, cmd.getProjectId()); @@ -54,7 +60,7 @@ public void testDedicateBgpPeerCmd() { Assert.assertEquals(1L, cmd.getEntityOwnerId()); Assert.assertEquals(EventTypes.EVENT_BGP_PEER_DEDICATE, cmd.getEventType()); - Assert.assertEquals(String.format("Dedicating Bgp Peer %s", id), cmd.getEventDescription()); + Assert.assertEquals(String.format("Dedicating BGP Peer with ID: %s", uuid), cmd.getEventDescription()); BgpPeer bgpPeer = Mockito.mock(BgpPeer.class); Mockito.when(routedIpv4Manager.dedicateBgpPeer(cmd)).thenReturn(bgpPeer); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/DeleteBgpPeerCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/DeleteBgpPeerCmdTest.java index 5e747188fda3..5228a63dc92d 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/DeleteBgpPeerCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/DeleteBgpPeerCmdTest.java @@ -20,6 +20,7 @@ import com.cloud.event.EventTypes; import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.network.RoutedIpv4Manager; import org.junit.Assert; import org.junit.Test; @@ -28,6 +29,8 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; +import java.util.UUID; + @RunWith(MockitoJUnitRunner.class) public class DeleteBgpPeerCmdTest { @@ -36,15 +39,18 @@ public class DeleteBgpPeerCmdTest { @Test public void testDeleteBgpPeerCmd() { Long id = 1L; + String uuid = UUID.randomUUID().toString(); DeleteBgpPeerCmd cmd = new DeleteBgpPeerCmd(); ReflectionTestUtils.setField(cmd, "id", id); ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + CallContext.current().putApiResourceUuid("id", uuid); + Assert.assertEquals(id, cmd.getId()); Assert.assertEquals(1L, cmd.getEntityOwnerId()); Assert.assertEquals(EventTypes.EVENT_BGP_PEER_DELETE, cmd.getEventType()); - Assert.assertEquals(String.format("Deleting Bgp Peer %s", id), cmd.getEventDescription()); + Assert.assertEquals(String.format("Deleting BGP Peer with ID: %s", uuid), cmd.getEventDescription()); Mockito.when(routedIpv4Manager.deleteBgpPeer(cmd)).thenReturn(true); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ReleaseDedicatedBgpPeerCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ReleaseDedicatedBgpPeerCmdTest.java index 8c55c4a73479..60a814d63050 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ReleaseDedicatedBgpPeerCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ReleaseDedicatedBgpPeerCmdTest.java @@ -19,6 +19,7 @@ import com.cloud.event.EventTypes; import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.network.BgpPeer; import org.apache.cloudstack.network.RoutedIpv4Manager; import org.junit.Assert; @@ -28,6 +29,8 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; +import java.util.UUID; + @RunWith(MockitoJUnitRunner.class) public class ReleaseDedicatedBgpPeerCmdTest { @@ -36,15 +39,18 @@ public class ReleaseDedicatedBgpPeerCmdTest { @Test public void testReleaseDedicatedBgpPeerCmd() { Long id = 1L; + String uuid = UUID.randomUUID().toString(); ReleaseDedicatedBgpPeerCmd cmd = new ReleaseDedicatedBgpPeerCmd(); ReflectionTestUtils.setField(cmd, "id", id); ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + CallContext.current().putApiResourceUuid("id", uuid); + Assert.assertEquals(id, cmd.getId()); Assert.assertEquals(1L, cmd.getEntityOwnerId()); Assert.assertEquals(EventTypes.EVENT_BGP_PEER_RELEASE, cmd.getEventType()); - Assert.assertEquals(String.format("Releasing a dedicated Bgp Peer %s", id), cmd.getEventDescription()); + Assert.assertEquals(String.format("Releasing dedicated BGP Peer with ID: %s", uuid), cmd.getEventDescription()); BgpPeer bgpPeer = Mockito.mock(BgpPeer.class); Mockito.when(routedIpv4Manager.releaseDedicatedBgpPeer(cmd)).thenReturn(bgpPeer); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/UpdateBgpPeerCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/UpdateBgpPeerCmdTest.java index 003944c61474..d594bc5718b7 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/UpdateBgpPeerCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/UpdateBgpPeerCmdTest.java @@ -20,6 +20,7 @@ import com.cloud.event.EventTypes; import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.network.BgpPeer; import org.apache.cloudstack.network.RoutedIpv4Manager; import org.junit.Assert; @@ -29,6 +30,8 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; +import java.util.UUID; + @RunWith(MockitoJUnitRunner.class) public class UpdateBgpPeerCmdTest { @@ -37,6 +40,7 @@ public class UpdateBgpPeerCmdTest { @Test public void testUpdateBgpPeerCmd() { Long id = 1L; + String uuid = UUID.randomUUID().toString(); String ip4Address = "ip4-address"; String ip6Address = "ip6-address"; Long peerAsNumber = 15000L; @@ -50,6 +54,8 @@ public void testUpdateBgpPeerCmd() { ReflectionTestUtils.setField(cmd, "password", peerPassword); ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + CallContext.current().putApiResourceUuid("id", uuid); + Assert.assertEquals(id, cmd.getId()); Assert.assertEquals(ip4Address, cmd.getIp4Address()); Assert.assertEquals(ip6Address, cmd.getIp6Address()); @@ -57,7 +63,7 @@ public void testUpdateBgpPeerCmd() { Assert.assertEquals(peerPassword, cmd.getPassword()); Assert.assertEquals(1L, cmd.getEntityOwnerId()); Assert.assertEquals(EventTypes.EVENT_BGP_PEER_UPDATE, cmd.getEventType()); - Assert.assertEquals(String.format("Updating Bgp Peer %s", id), cmd.getEventDescription()); + Assert.assertEquals(String.format("Updating BGP Peer with ID: %s", uuid), cmd.getEventDescription()); BgpPeer bgpPeer = Mockito.mock(BgpPeer.class); Mockito.when(routedIpv4Manager.updateBgpPeer(cmd)).thenReturn(bgpPeer); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CloneServiceOfferingCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CloneServiceOfferingCmdTest.java new file mode 100644 index 000000000000..b4f7c55bd1fa --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CloneServiceOfferingCmdTest.java @@ -0,0 +1,669 @@ +// 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.offering; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.offering.ServiceOffering; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ServiceOfferingResponse; +import org.apache.cloudstack.vm.lease.VMLeaseManager; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.HashMap; +import java.util.Map; + +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.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class CloneServiceOfferingCmdTest { + + private CloneServiceOfferingCmd cloneServiceOfferingCmd; + + @Mock + private com.cloud.configuration.ConfigurationService configService; + + @Mock + private ResponseGenerator responseGenerator; + + @Mock + private ServiceOffering mockServiceOffering; + + @Mock + private ServiceOfferingResponse mockServiceOfferingResponse; + + @Before + public void setUp() { + cloneServiceOfferingCmd = new CloneServiceOfferingCmd(); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "_configService", configService); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "_responseGenerator", responseGenerator); + } + + @Test + public void testGetSourceOfferingId() { + Long sourceOfferingId = 555L; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "sourceOfferingId", sourceOfferingId); + assertEquals(sourceOfferingId, cloneServiceOfferingCmd.getSourceOfferingId()); + } + + @Test + public void testGetServiceOfferingName() { + String name = "ClonedServiceOffering"; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "serviceOfferingName", name); + assertEquals(name, cloneServiceOfferingCmd.getServiceOfferingName()); + } + + @Test + public void testGetDisplayText() { + String displayText = "Cloned Service Offering Display Text"; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "displayText", displayText); + assertEquals(displayText, cloneServiceOfferingCmd.getDisplayText()); + } + + @Test + public void testGetDisplayTextDefaultsToName() { + String name = "ClonedServiceOffering"; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "serviceOfferingName", name); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "displayText", null); + assertEquals(name, cloneServiceOfferingCmd.getDisplayText()); + } + + @Test + public void testGetCpu() { + Integer cpu = 4; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "cpuNumber", cpu); + assertEquals(cpu, cloneServiceOfferingCmd.getCpuNumber()); + } + + @Test + public void testGetMemory() { + Integer memory = 8192; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "memory", memory); + assertEquals(memory, cloneServiceOfferingCmd.getMemory()); + } + + @Test + public void testGetCpuSpeed() { + Integer cpuSpeed = 2000; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "cpuSpeed", cpuSpeed); + assertEquals(cpuSpeed, cloneServiceOfferingCmd.getCpuSpeed()); + } + + @Test + public void testGetOfferHa() { + Boolean offerHa = true; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "offerHa", offerHa); + assertEquals(offerHa, cloneServiceOfferingCmd.isOfferHa()); + } + + @Test + public void testGetLimitCpuUse() { + Boolean limitCpuUse = false; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "limitCpuUse", limitCpuUse); + assertEquals(limitCpuUse, cloneServiceOfferingCmd.isLimitCpuUse()); + } + + @Test + public void testGetVolatileVm() { + Boolean volatileVm = true; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "isVolatile", volatileVm); + assertEquals(volatileVm, cloneServiceOfferingCmd.isVolatileVm()); + } + + @Test + public void testGetStorageType() { + String storageType = "local"; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "storageType", storageType); + assertEquals(storageType, cloneServiceOfferingCmd.getStorageType()); + } + + @Test + public void testGetTags() { + String tags = "ssd,premium,dedicated"; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "tags", tags); + assertEquals(tags, cloneServiceOfferingCmd.getTags()); + } + + @Test + public void testGetHostTag() { + String hostTag = "gpu-enabled"; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "hostTag", hostTag); + assertEquals(hostTag, cloneServiceOfferingCmd.getHostTag()); + } + + @Test + public void testGetNetworkRate() { + Integer networkRate = 1000; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "networkRate", networkRate); + assertEquals(networkRate, cloneServiceOfferingCmd.getNetworkRate()); + } + + @Test + public void testGetDeploymentPlanner() { + String deploymentPlanner = "UserDispersingPlanner"; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "deploymentPlanner", deploymentPlanner); + assertEquals(deploymentPlanner, cloneServiceOfferingCmd.getDeploymentPlanner()); + } + + @Test + public void testGetDetails() { + Map> details = new HashMap<>(); + + HashMap cpuOvercommit = new HashMap<>(); + cpuOvercommit.put("key", "cpuOvercommitRatio"); + cpuOvercommit.put("value", "2.0"); + + HashMap memoryOvercommit = new HashMap<>(); + memoryOvercommit.put("key", "memoryOvercommitRatio"); + memoryOvercommit.put("value", "1.5"); + + details.put("0", cpuOvercommit); + details.put("1", memoryOvercommit); + + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "details", details); + + Map result = cloneServiceOfferingCmd.getDetails(); + assertNotNull(result); + assertEquals("2.0", result.get("cpuOvercommitRatio")); + assertEquals("1.5", result.get("memoryOvercommitRatio")); + } + + @Test + public void testIsPurgeResources() { + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "purgeResources", true); + assertTrue(cloneServiceOfferingCmd.isPurgeResources()); + } + + @Test + public void testIsPurgeResourcesFalse() { + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "purgeResources", false); + assertFalse(cloneServiceOfferingCmd.isPurgeResources()); + } + + @Test + public void testIsPurgeResourcesDefaultFalse() { + assertFalse(cloneServiceOfferingCmd.isPurgeResources()); + } + + @Test + public void testGetLeaseDuration() { + Integer leaseDuration = 3600; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "leaseDuration", leaseDuration); + assertEquals(leaseDuration, cloneServiceOfferingCmd.getLeaseDuration()); + } + + @Test + public void testGetLeaseExpiryAction() { + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "leaseExpiryAction", "stop"); + assertEquals(VMLeaseManager.ExpiryAction.STOP, cloneServiceOfferingCmd.getLeaseExpiryAction()); + + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "leaseExpiryAction", "DESTROY"); + assertEquals(VMLeaseManager.ExpiryAction.DESTROY, cloneServiceOfferingCmd.getLeaseExpiryAction()); + } + + @Test(expected = InvalidParameterValueException.class) + public void testGetLeaseExpiryActionInvalidValue() { + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "leaseExpiryAction", "InvalidAction"); + cloneServiceOfferingCmd.getLeaseExpiryAction(); + } + + @Test + public void testGetVgpuProfileId() { + Long vgpuProfileId = 10L; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "vgpuProfileId", vgpuProfileId); + assertEquals(vgpuProfileId, cloneServiceOfferingCmd.getVgpuProfileId()); + } + + @Test + public void testGetGpuCount() { + Integer gpuCount = 2; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "gpuCount", gpuCount); + assertEquals(gpuCount, cloneServiceOfferingCmd.getGpuCount()); + } + + @Test + public void testGetGpuDisplay() { + Boolean gpuDisplay = true; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "gpuDisplay", gpuDisplay); + assertEquals(gpuDisplay, cloneServiceOfferingCmd.getGpuDisplay()); + } + + @Test + public void testExecuteSuccess() { + Long sourceOfferingId = 555L; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "sourceOfferingId", sourceOfferingId); + + when(configService.cloneServiceOffering(any(CloneServiceOfferingCmd.class))).thenReturn(mockServiceOffering); + when(responseGenerator.createServiceOfferingResponse(mockServiceOffering)).thenReturn(mockServiceOfferingResponse); + + cloneServiceOfferingCmd.execute(); + + assertNotNull(cloneServiceOfferingCmd.getResponseObject()); + assertEquals(mockServiceOfferingResponse, cloneServiceOfferingCmd.getResponseObject()); + } + + @Test + public void testExecuteFailure() { + Long sourceOfferingId = 555L; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "sourceOfferingId", sourceOfferingId); + + when(configService.cloneServiceOffering(any(CloneServiceOfferingCmd.class))).thenReturn(null); + + try { + cloneServiceOfferingCmd.execute(); + fail("Expected ServerApiException to be thrown"); + } catch (ServerApiException e) { + assertEquals(ApiErrorCode.INTERNAL_ERROR, e.getErrorCode()); + assertEquals("Failed to clone service offering", e.getMessage()); + } + } + + @Test + public void testExecuteSuccessWithAllParameters() { + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "sourceOfferingId", 555L); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "serviceOfferingName", "ClonedOffering"); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "displayText", "Test Display"); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "cpuNumber", 4); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "memory", 8192); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "cpuSpeed", 2000); + + when(configService.cloneServiceOffering(any(CloneServiceOfferingCmd.class))).thenReturn(mockServiceOffering); + when(responseGenerator.createServiceOfferingResponse(mockServiceOffering)).thenReturn(mockServiceOfferingResponse); + + cloneServiceOfferingCmd.execute(); + + assertNotNull(cloneServiceOfferingCmd.getResponseObject()); + assertEquals(mockServiceOfferingResponse, cloneServiceOfferingCmd.getResponseObject()); + } + + @Test + public void testExecuteWithInvalidParameterException() { + Long sourceOfferingId = 555L; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "sourceOfferingId", sourceOfferingId); + + when(configService.cloneServiceOffering(any(CloneServiceOfferingCmd.class))) + .thenThrow(new InvalidParameterValueException("Invalid source offering ID")); + + try { + cloneServiceOfferingCmd.execute(); + fail("Expected ServerApiException to be thrown"); + } catch (ServerApiException e) { + assertEquals(ApiErrorCode.PARAM_ERROR, e.getErrorCode()); + assertEquals("Invalid source offering ID", e.getMessage()); + } + } + + @Test + public void testExecuteWithCloudRuntimeException() { + Long sourceOfferingId = 555L; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "sourceOfferingId", sourceOfferingId); + + when(configService.cloneServiceOffering(any(CloneServiceOfferingCmd.class))) + .thenThrow(new com.cloud.utils.exception.CloudRuntimeException("Runtime error during clone")); + + try { + cloneServiceOfferingCmd.execute(); + fail("Expected ServerApiException to be thrown"); + } catch (ServerApiException e) { + assertEquals(ApiErrorCode.INTERNAL_ERROR, e.getErrorCode()); + assertEquals("Runtime error during clone", e.getMessage()); + } + } + + @Test + public void testExecuteResponseNameIsSet() { + Long sourceOfferingId = 555L; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "sourceOfferingId", sourceOfferingId); + + when(configService.cloneServiceOffering(any(CloneServiceOfferingCmd.class))).thenReturn(mockServiceOffering); + when(responseGenerator.createServiceOfferingResponse(mockServiceOffering)).thenReturn(mockServiceOfferingResponse); + + cloneServiceOfferingCmd.execute(); + + assertNotNull(cloneServiceOfferingCmd.getResponseObject()); + // Verify that response name would be set (actual verification would require accessing the response object's internal state) + } + + @Test + public void testCloneWithAllParameters() { + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "sourceOfferingId", 555L); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "serviceOfferingName", "ClonedServiceOffering"); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "displayText", "Cloned Service Offering"); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "cpuNumber", 4); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "memory", 8192); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "cpuSpeed", 2000); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "offerHa", true); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "limitCpuUse", false); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "isVolatile", true); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "storageType", "local"); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "tags", "premium"); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "hostTag", "gpu-enabled"); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "networkRate", 1000); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "deploymentPlanner", "UserDispersingPlanner"); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "purgeResources", true); + + assertEquals(Long.valueOf(555L), cloneServiceOfferingCmd.getSourceOfferingId()); + assertEquals("ClonedServiceOffering", cloneServiceOfferingCmd.getServiceOfferingName()); + assertEquals("Cloned Service Offering", cloneServiceOfferingCmd.getDisplayText()); + assertEquals(Integer.valueOf(4), cloneServiceOfferingCmd.getCpuNumber()); + assertEquals(Integer.valueOf(8192), cloneServiceOfferingCmd.getMemory()); + assertEquals(Integer.valueOf(2000), cloneServiceOfferingCmd.getCpuSpeed()); + assertEquals(Boolean.TRUE, cloneServiceOfferingCmd.isOfferHa()); + assertEquals(Boolean.FALSE, cloneServiceOfferingCmd.isLimitCpuUse()); + assertEquals(Boolean.TRUE, cloneServiceOfferingCmd.isVolatileVm()); + assertEquals("local", cloneServiceOfferingCmd.getStorageType()); + assertEquals("premium", cloneServiceOfferingCmd.getTags()); + assertEquals("gpu-enabled", cloneServiceOfferingCmd.getHostTag()); + assertEquals(Integer.valueOf(1000), cloneServiceOfferingCmd.getNetworkRate()); + assertEquals("UserDispersingPlanner", cloneServiceOfferingCmd.getDeploymentPlanner()); + assertTrue(cloneServiceOfferingCmd.isPurgeResources()); + } + + @Test + public void testSourceOfferingIdNullByDefault() { + assertNull(cloneServiceOfferingCmd.getSourceOfferingId()); + } + + @Test + public void testGetSystemVmType() { + String systemVmType = "domainrouter"; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "systemVmType", systemVmType); + assertEquals(systemVmType, cloneServiceOfferingCmd.getSystemVmType()); + } + + @Test + public void testGetBytesReadRate() { + Long bytesReadRate = 1000000L; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "bytesReadRate", bytesReadRate); + assertEquals(bytesReadRate, cloneServiceOfferingCmd.getBytesReadRate()); + } + + @Test + public void testGetBytesWriteRate() { + Long bytesWriteRate = 1000000L; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "bytesWriteRate", bytesWriteRate); + assertEquals(bytesWriteRate, cloneServiceOfferingCmd.getBytesWriteRate()); + } + + @Test + public void testGetIopsReadRate() { + Long iopsReadRate = 1000L; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "iopsReadRate", iopsReadRate); + assertEquals(iopsReadRate, cloneServiceOfferingCmd.getIopsReadRate()); + } + + @Test + public void testGetIopsWriteRate() { + Long iopsWriteRate = 1000L; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "iopsWriteRate", iopsWriteRate); + assertEquals(iopsWriteRate, cloneServiceOfferingCmd.getIopsWriteRate()); + } + + @Test + public void testCloneServiceOfferingWithGpuProfile() { + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "sourceOfferingId", 555L); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "serviceOfferingName", "GPU-Offering-Clone"); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "vgpuProfileId", 10L); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "gpuCount", 2); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "gpuDisplay", true); + + assertEquals(Long.valueOf(10L), cloneServiceOfferingCmd.getVgpuProfileId()); + assertEquals(Integer.valueOf(2), cloneServiceOfferingCmd.getGpuCount()); + assertTrue(cloneServiceOfferingCmd.getGpuDisplay()); + } + + @Test + public void testCloneServiceOfferingWithLease() { + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "sourceOfferingId", 555L); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "serviceOfferingName", "Lease-Offering-Clone"); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "leaseDuration", 7200); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "leaseExpiryAction", "destroy"); + + assertEquals(Integer.valueOf(7200), cloneServiceOfferingCmd.getLeaseDuration()); + assertEquals(VMLeaseManager.ExpiryAction.DESTROY, cloneServiceOfferingCmd.getLeaseExpiryAction()); + } + + @Test + public void testExecuteWithOverriddenParameters() { + Long sourceOfferingId = 555L; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "sourceOfferingId", sourceOfferingId); + + String newName = "ClonedOffering-Override"; + String newDisplayText = "Overridden Display Text"; + Integer newCpu = 8; + Integer newMemory = 16384; + Integer newCpuSpeed = 3000; + Boolean newOfferHa = true; + Boolean newLimitCpuUse = true; + String newStorageType = "shared"; + String newTags = "premium,gpu"; + String newHostTag = "compute-optimized"; + Integer newNetworkRate = 2000; + String newDeploymentPlanner = "FirstFitPlanner"; + Boolean newPurgeResources = true; + + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "serviceOfferingName", newName); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "displayText", newDisplayText); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "cpuNumber", newCpu); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "memory", newMemory); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "cpuSpeed", newCpuSpeed); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "offerHa", newOfferHa); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "limitCpuUse", newLimitCpuUse); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "storageType", newStorageType); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "tags", newTags); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "hostTag", newHostTag); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "networkRate", newNetworkRate); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "deploymentPlanner", newDeploymentPlanner); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "purgeResources", newPurgeResources); + + assertEquals(sourceOfferingId, cloneServiceOfferingCmd.getSourceOfferingId()); + assertEquals(newName, cloneServiceOfferingCmd.getServiceOfferingName()); + assertEquals(newDisplayText, cloneServiceOfferingCmd.getDisplayText()); + assertEquals(newCpu, cloneServiceOfferingCmd.getCpuNumber()); + assertEquals(newMemory, cloneServiceOfferingCmd.getMemory()); + assertEquals(newCpuSpeed, cloneServiceOfferingCmd.getCpuSpeed()); + assertEquals(newOfferHa, cloneServiceOfferingCmd.isOfferHa()); + assertEquals(newLimitCpuUse, cloneServiceOfferingCmd.isLimitCpuUse()); + assertEquals(newStorageType, cloneServiceOfferingCmd.getStorageType()); + assertEquals(newTags, cloneServiceOfferingCmd.getTags()); + assertEquals(newHostTag, cloneServiceOfferingCmd.getHostTag()); + assertEquals(newNetworkRate, cloneServiceOfferingCmd.getNetworkRate()); + assertEquals(newDeploymentPlanner, cloneServiceOfferingCmd.getDeploymentPlanner()); + assertTrue(cloneServiceOfferingCmd.isPurgeResources()); + + when(configService.cloneServiceOffering(any(CloneServiceOfferingCmd.class))).thenReturn(mockServiceOffering); + when(responseGenerator.createServiceOfferingResponse(mockServiceOffering)).thenReturn(mockServiceOfferingResponse); + + cloneServiceOfferingCmd.execute(); + + assertNotNull(cloneServiceOfferingCmd.getResponseObject()); + assertEquals(mockServiceOfferingResponse, cloneServiceOfferingCmd.getResponseObject()); + } + + @Test + public void testExecuteWithPartialOverrides() { + Long sourceOfferingId = 555L; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "sourceOfferingId", sourceOfferingId); + + String newName = "PartialOverride"; + Integer newCpu = 6; + Integer newMemory = 12288; + + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "serviceOfferingName", newName); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "cpuNumber", newCpu); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "memory", newMemory); + + assertEquals(newName, cloneServiceOfferingCmd.getServiceOfferingName()); + assertEquals(newCpu, cloneServiceOfferingCmd.getCpuNumber()); + assertEquals(newMemory, cloneServiceOfferingCmd.getMemory()); + + assertNull(cloneServiceOfferingCmd.getCpuSpeed()); + assertFalse(cloneServiceOfferingCmd.isOfferHa()); + assertNull(cloneServiceOfferingCmd.getStorageType()); + + when(configService.cloneServiceOffering(any(CloneServiceOfferingCmd.class))).thenReturn(mockServiceOffering); + when(responseGenerator.createServiceOfferingResponse(mockServiceOffering)).thenReturn(mockServiceOfferingResponse); + + cloneServiceOfferingCmd.execute(); + + assertNotNull(cloneServiceOfferingCmd.getResponseObject()); + assertEquals(mockServiceOfferingResponse, cloneServiceOfferingCmd.getResponseObject()); + } + + @Test + public void testExecuteWithGpuOverrides() { + Long sourceOfferingId = 555L; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "sourceOfferingId", sourceOfferingId); + + String newName = "GPU-Clone-Override"; + Long vgpuProfileId = 15L; + Integer gpuCount = 4; + Boolean gpuDisplay = false; + + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "serviceOfferingName", newName); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "vgpuProfileId", vgpuProfileId); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "gpuCount", gpuCount); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "gpuDisplay", gpuDisplay); + + assertEquals(newName, cloneServiceOfferingCmd.getServiceOfferingName()); + assertEquals(vgpuProfileId, cloneServiceOfferingCmd.getVgpuProfileId()); + assertEquals(gpuCount, cloneServiceOfferingCmd.getGpuCount()); + assertEquals(gpuDisplay, cloneServiceOfferingCmd.getGpuDisplay()); + + when(configService.cloneServiceOffering(any(CloneServiceOfferingCmd.class))).thenReturn(mockServiceOffering); + when(responseGenerator.createServiceOfferingResponse(mockServiceOffering)).thenReturn(mockServiceOfferingResponse); + + cloneServiceOfferingCmd.execute(); + + assertNotNull(cloneServiceOfferingCmd.getResponseObject()); + assertEquals(mockServiceOfferingResponse, cloneServiceOfferingCmd.getResponseObject()); + } + + @Test + public void testExecuteWithLeaseOverrides() { + Long sourceOfferingId = 555L; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "sourceOfferingId", sourceOfferingId); + + String newName = "Lease-Clone-Override"; + Integer leaseDuration = 14400; // 4 hours + String leaseExpiryAction = "stop"; + + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "serviceOfferingName", newName); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "leaseDuration", leaseDuration); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "leaseExpiryAction", leaseExpiryAction); + + assertEquals(newName, cloneServiceOfferingCmd.getServiceOfferingName()); + assertEquals(leaseDuration, cloneServiceOfferingCmd.getLeaseDuration()); + assertEquals(VMLeaseManager.ExpiryAction.STOP, cloneServiceOfferingCmd.getLeaseExpiryAction()); + + when(configService.cloneServiceOffering(any(CloneServiceOfferingCmd.class))).thenReturn(mockServiceOffering); + when(responseGenerator.createServiceOfferingResponse(mockServiceOffering)).thenReturn(mockServiceOfferingResponse); + + cloneServiceOfferingCmd.execute(); + + assertNotNull(cloneServiceOfferingCmd.getResponseObject()); + assertEquals(mockServiceOfferingResponse, cloneServiceOfferingCmd.getResponseObject()); + } + + @Test + public void testExecuteWithStorageOverrides() { + Long sourceOfferingId = 555L; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "sourceOfferingId", sourceOfferingId); + String newName = "Storage-Clone-Override"; + Long bytesReadRate = 2000000L; + Long bytesWriteRate = 1500000L; + Long iopsReadRate = 2000L; + Long iopsWriteRate = 1500L; + + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "serviceOfferingName", newName); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "bytesReadRate", bytesReadRate); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "bytesWriteRate", bytesWriteRate); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "iopsReadRate", iopsReadRate); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "iopsWriteRate", iopsWriteRate); + + assertEquals(newName, cloneServiceOfferingCmd.getServiceOfferingName()); + assertEquals(bytesReadRate, cloneServiceOfferingCmd.getBytesReadRate()); + assertEquals(bytesWriteRate, cloneServiceOfferingCmd.getBytesWriteRate()); + assertEquals(iopsReadRate, cloneServiceOfferingCmd.getIopsReadRate()); + assertEquals(iopsWriteRate, cloneServiceOfferingCmd.getIopsWriteRate()); + + when(configService.cloneServiceOffering(any(CloneServiceOfferingCmd.class))).thenReturn(mockServiceOffering); + when(responseGenerator.createServiceOfferingResponse(mockServiceOffering)).thenReturn(mockServiceOfferingResponse); + + cloneServiceOfferingCmd.execute(); + + assertNotNull(cloneServiceOfferingCmd.getResponseObject()); + assertEquals(mockServiceOfferingResponse, cloneServiceOfferingCmd.getResponseObject()); + } + + @Test + public void testExecuteWithDetailsOverride() { + Long sourceOfferingId = 555L; + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "sourceOfferingId", sourceOfferingId); + + String newName = "Details-Clone-Override"; + Map> details = new HashMap<>(); + + HashMap cpuOvercommit = new HashMap<>(); + cpuOvercommit.put("key", "cpuOvercommitRatio"); + cpuOvercommit.put("value", "3.0"); + + HashMap memoryOvercommit = new HashMap<>(); + memoryOvercommit.put("key", "memoryOvercommitRatio"); + memoryOvercommit.put("value", "2.5"); + + HashMap customDetail = new HashMap<>(); + customDetail.put("key", "customParameter"); + customDetail.put("value", "customValue"); + + details.put("0", cpuOvercommit); + details.put("1", memoryOvercommit); + details.put("2", customDetail); + + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "serviceOfferingName", newName); + ReflectionTestUtils.setField(cloneServiceOfferingCmd, "details", details); + + assertEquals(newName, cloneServiceOfferingCmd.getServiceOfferingName()); + Map result = cloneServiceOfferingCmd.getDetails(); + assertNotNull(result); + assertEquals("3.0", result.get("cpuOvercommitRatio")); + assertEquals("2.5", result.get("memoryOvercommitRatio")); + assertEquals("customValue", result.get("customParameter")); + + when(configService.cloneServiceOffering(any(CloneServiceOfferingCmd.class))).thenReturn(mockServiceOffering); + when(responseGenerator.createServiceOfferingResponse(mockServiceOffering)).thenReturn(mockServiceOfferingResponse); + + cloneServiceOfferingCmd.execute(); + + assertNotNull(cloneServiceOfferingCmd.getResponseObject()); + assertEquals(mockServiceOfferingResponse, cloneServiceOfferingCmd.getResponseObject()); + } +} 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 ad95ce10bd6c..8d2771c969b7 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 @@ -19,6 +19,7 @@ import com.cloud.utils.Pair; import org.apache.cloudstack.api.response.ExtractResponse; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.storage.browser.StorageBrowser; import org.junit.Assert; import org.junit.Test; @@ -30,6 +31,7 @@ import org.springframework.test.util.ReflectionTestUtils; import java.util.List; +import java.util.UUID; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -95,10 +97,13 @@ public void testGetEventType() { @Test public void testGetEventDescription() { + String uuid = UUID.randomUUID().toString(); + ReflectionTestUtils.setField(cmd, "storeId", 1L); ReflectionTestUtils.setField(cmd, "path", "path/to/object"); + CallContext.current().putApiResourceUuid("id", uuid); String eventDescription = cmd.getEventDescription(); - Assert.assertEquals("Downloading object at path path/to/object on image store 1", eventDescription); + Assert.assertEquals(String.format("Downloading object at path path/to/object on image store %s", uuid), eventDescription); } } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/user/CreateUserCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/user/CreateUserCmdTest.java index 8a57ac3eb22c..397723dd6069 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/user/CreateUserCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/user/CreateUserCmdTest.java @@ -69,7 +69,7 @@ public void testExecuteWithNotBlankPassword() { } catch (ServerApiException e) { Assert.assertTrue("Received exception as the mock accountService createUser returns null user", true); } - Mockito.verify(accountService, Mockito.times(1)).createUser(null, "Test", null, null, null, null, null, null, null); + Mockito.verify(accountService, Mockito.times(1)).createUser(null, "Test", null, null, null, null, null, null, null, false); } @Test @@ -82,7 +82,7 @@ public void testExecuteWithNullPassword() { Assert.assertEquals(ApiErrorCode.PARAM_ERROR,e.getErrorCode()); Assert.assertEquals("Empty passwords are not allowed", e.getMessage()); } - Mockito.verify(accountService, Mockito.never()).createUser(null, null, null, null, null, null, null, null, null); + Mockito.verify(accountService, Mockito.never()).createUser(null, null, null, null, null, null, null, null, null, false); } @Test @@ -95,6 +95,6 @@ public void testExecuteWithEmptyPassword() { Assert.assertEquals(ApiErrorCode.PARAM_ERROR,e.getErrorCode()); Assert.assertEquals("Empty passwords are not allowed", e.getMessage()); } - Mockito.verify(accountService, Mockito.never()).createUser(null, null, null, null, null, null, null, null, null); + Mockito.verify(accountService, Mockito.never()).createUser(null, null, null, null, null, null, null, null, null, true); } } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/user/UpdateUserCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/user/UpdateUserCmdTest.java new file mode 100644 index 000000000000..f86e51adb5ab --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/user/UpdateUserCmdTest.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 org.apache.cloudstack.api.command.admin.user; + +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class UpdateUserCmdTest { + @InjectMocks + private UpdateUserCmd cmd; + + @Test + public void testGetApiResourceId() { + Long userId = 99L; + cmd.setId(userId); + Assert.assertEquals(userId, cmd.getApiResourceId()); + } + + @Test + public void testGetApiResourceType() { + Assert.assertEquals(ApiCommandResourceType.User, cmd.getApiResourceType()); + } + + @Test + public void testIsPasswordChangeRequired_True() { + ReflectionTestUtils.setField(cmd, "passwordChangeRequired", Boolean.TRUE); + Assert.assertTrue(cmd.isPasswordChangeRequired()); + } + + @Test + public void testIsPasswordChangeRequired_False() { + ReflectionTestUtils.setField(cmd, "passwordChangeRequired", Boolean.FALSE); + Assert.assertFalse(cmd.isPasswordChangeRequired()); + } + + @Test + public void testIsPasswordChangeRequired_Null() { + ReflectionTestUtils.setField(cmd, "passwordChangeRequired", null); + Assert.assertFalse(cmd.isPasswordChangeRequired()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/vlan/UpdateVlanIpRangeCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/vlan/UpdateVlanIpRangeCmdTest.java index c1bf6b151f7f..f2c23edbc7fe 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/vlan/UpdateVlanIpRangeCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/vlan/UpdateVlanIpRangeCmdTest.java @@ -71,7 +71,7 @@ public void testUpdateFailure() throws ResourceAllocationException, ResourceUnav try { updateVlanIpRangeCmd.execute(); } catch (ServerApiException ex) { - assertEquals("Failed to Update vlan ip range", ex.getMessage()); + assertEquals("Failed to Update VLAN IP range", ex.getMessage()); } } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/AssignVMCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/AssignVMCmdTest.java new file mode 100644 index 000000000000..27bc4614e1b5 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/AssignVMCmdTest.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.junit.Assert; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +public class AssignVMCmdTest { + + @Test + public void test_setSkipNetwork_default() { + AssignVMCmd assignVMCmd = new AssignVMCmd(); + Object value = ReflectionTestUtils.getField(assignVMCmd, "skipNetwork"); + Assert.assertTrue(value instanceof Boolean); + Assert.assertFalse((Boolean) value); + } + + @Test + public void test_setSkipNetwork_set() { + AssignVMCmd assignVMCmd = new AssignVMCmd(); + assignVMCmd.setSkipNetwork(true); + Object value = ReflectionTestUtils.getField(assignVMCmd, "skipNetwork"); + Assert.assertTrue(value instanceof Boolean); + Assert.assertTrue((Boolean) value); + } + + @Test + public void test_isSkipNetwork_default() { + AssignVMCmd assignVMCmd = new AssignVMCmd(); + Assert.assertFalse(assignVMCmd.isSkipNetwork()); + } + + @Test + public void test_isSkipNetwork_set() { + AssignVMCmd assignVMCmd = new AssignVMCmd(); + ReflectionTestUtils.setField(assignVMCmd, "skipNetwork", true); + Assert.assertTrue(assignVMCmd.isSkipNetwork()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/DeployVMCmdByAdminTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/DeployVMCmdByAdminTest.java new file mode 100644 index 000000000000..d82dc9f766f9 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/DeployVMCmdByAdminTest.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.vm; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class DeployVMCmdByAdminTest { + + @InjectMocks + private DeployVMCmdByAdmin cmd; + + @Test + public void testIsBlankInstance_default() { + assertFalse(cmd.isBlankInstance()); + } + + @Test + public void testIsBlankInstance_true() { + ReflectionTestUtils.setField(cmd, "blankInstance", true); + assertTrue(cmd.isBlankInstance()); + } + + @Test + public void testIsBlankInstance_false() { + ReflectionTestUtils.setField(cmd, "blankInstance", false); + assertFalse(cmd.isBlankInstance()); + } + + @Test + public void testSetBlankInstance_default() { + Object obj = ReflectionTestUtils.getField(cmd, "blankInstance"); + assertNull(obj); + } + + @Test + public void testSetBlankInstance_true() { + cmd.setBlankInstance(true); + Object obj = ReflectionTestUtils.getField(cmd, "blankInstance"); + assertNotNull(obj); + assertTrue((boolean)obj); + } + + @Test + public void testSetBlankInstance_false() { + cmd.setBlankInstance(false); + Object obj = ReflectionTestUtils.getField(cmd, "blankInstance"); + assertNotNull(obj); + assertFalse((boolean)obj); + } +} 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 4fec90217fd8..c0e3b1aba321 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 @@ -103,7 +103,7 @@ public void executeTestRequiredArgsNullThrowsInvalidParameterValueException() { cmdSpy.execute(); } catch (Exception e) { Assert.assertEquals(InvalidParameterValueException.class, e.getClass()); - String expected = String.format("Either %s or %s must be passed or %s must be true for migrating the VM.", ApiConstants.HOST_ID, ApiConstants.MIGRATE_TO, ApiConstants.AUTO_SELECT); + String expected = String.format("Either %s or %s must be passed or %s must be true for migrating the Instance.", ApiConstants.HOST_ID, ApiConstants.MIGRATE_TO, ApiConstants.AUTO_SELECT); Assert.assertEquals(expected , e.getMessage()); } } @@ -161,7 +161,7 @@ public void executeTestHostIdIsNullThrowsInvalidParameterValueException() { cmdSpy.execute(); } catch (Exception e) { Assert.assertEquals(InvalidParameterValueException.class, e.getClass()); - String expected = "Unable to find the specified host to migrate the VM."; + String expected = "Unable to find the specified host to migrate the Instance."; Assert.assertEquals(expected , e.getMessage()); } } @@ -187,7 +187,7 @@ public void executeTestHostIsNotNullMigratedVMIsNullThrowsServerApiException() t cmdSpy.execute(); } catch (Exception e) { Assert.assertEquals(ServerApiException.class, e.getClass()); - String expected = "Failed to migrate vm"; + String expected = "Failed to migrate Instance"; Assert.assertEquals(expected , e.getMessage()); } } @@ -205,7 +205,7 @@ public void executeTestHostIsNullMigratedVMIsNullThrowsServerApiException() { cmdSpy.execute(); } catch (Exception e) { Assert.assertEquals(ServerApiException.class, e.getClass()); - String expected = "Failed to migrate vm"; + String expected = "Failed to migrate Instance"; Assert.assertEquals(expected , e.getMessage()); } } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/volume/ImportVolumeCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/volume/ImportVolumeCmdTest.java index a7c41b9271b1..235acb15eeab 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/volume/ImportVolumeCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/volume/ImportVolumeCmdTest.java @@ -46,7 +46,7 @@ public void testImportVolumeCmd() { Long projectId = 5L; long accountId = 6L; - Mockito.when(accountService.finalyzeAccountId(accountName, domainId, projectId, true)).thenReturn(accountId); + Mockito.when(accountService.finalizeAccountId(accountName, domainId, projectId, true)).thenReturn(accountId); ImportVolumeCmd cmd = new ImportVolumeCmd(); ReflectionTestUtils.setField(cmd, "path", path); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/volume/UnmanageVolumeCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/volume/UnmanageVolumeCmdTest.java index ba7e351a8a8e..59a61806e867 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/volume/UnmanageVolumeCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/volume/UnmanageVolumeCmdTest.java @@ -22,6 +22,7 @@ import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ResponseGenerator; import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.storage.volume.VolumeImportUnmanageService; import org.junit.Assert; import org.junit.Test; @@ -31,6 +32,8 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; +import java.util.UUID; + @RunWith(MockitoJUnitRunner.class) public class UnmanageVolumeCmdTest { @@ -41,6 +44,7 @@ public class UnmanageVolumeCmdTest { public void testUnmanageVolumeCmd() { long accountId = 2L; Long volumeId = 3L; + String volumeUuid = UUID.randomUUID().toString(); Volume volume = Mockito.mock(Volume.class); Mockito.when(responseGenerator.findVolumeById(volumeId)).thenReturn(volume); @@ -51,12 +55,14 @@ public void testUnmanageVolumeCmd() { ReflectionTestUtils.setField(cmd,"volumeImportService", volumeImportService); ReflectionTestUtils.setField(cmd,"_responseGenerator", responseGenerator); + CallContext.current().putApiResourceUuid("id", volumeUuid); + Assert.assertEquals(volumeId, cmd.getVolumeId()); Assert.assertEquals(accountId, cmd.getEntityOwnerId()); Assert.assertEquals(volumeId, cmd.getApiResourceId()); Assert.assertEquals(ApiCommandResourceType.Volume, cmd.getApiResourceType()); Assert.assertEquals(EventTypes.EVENT_VOLUME_UNMANAGE, cmd.getEventType()); - Assert.assertEquals("Unmanaging Volume with ID " + volumeId, cmd.getEventDescription()); + Assert.assertEquals("Unmanaging Volume with ID: " + volumeUuid, cmd.getEventDescription()); Mockito.when(volumeImportService.unmanageVolume(volumeId)).thenReturn(true); try { diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CloneVpcOfferingCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CloneVpcOfferingCmdTest.java new file mode 100644 index 000000000000..1e6d6c9e0969 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CloneVpcOfferingCmdTest.java @@ -0,0 +1,299 @@ +// 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.vpc; + +import com.cloud.network.vpc.VpcOffering; +import com.cloud.network.vpc.VpcProvisioningService; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.VpcOfferingResponse; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Arrays; +import java.util.HashMap; +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.assertNull; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class CloneVpcOfferingCmdTest { + + private CloneVPCOfferingCmd cloneVpcOfferingCmd; + + @Mock + private VpcProvisioningService vpcService; + + @Mock + private ResponseGenerator responseGenerator; + + @Mock + private VpcOffering mockVpcOffering; + + @Mock + private VpcOfferingResponse mockVpcOfferingResponse; + + @Before + public void setUp() { + cloneVpcOfferingCmd = new CloneVPCOfferingCmd(); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "_vpcProvSvc", vpcService); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "_responseGenerator", responseGenerator); + } + + @Test + public void testGetSourceOfferingId() { + Long sourceOfferingId = 789L; + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "sourceOfferingId", sourceOfferingId); + assertEquals(sourceOfferingId, cloneVpcOfferingCmd.getSourceOfferingId()); + } + + @Test + public void testGetName() { + String name = "ClonedVpcOffering"; + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "vpcOfferingName", name); + assertEquals(name, cloneVpcOfferingCmd.getVpcOfferingName()); + } + + @Test + public void testGetDisplayText() { + String displayText = "Cloned VPC Offering Display Text"; + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "displayText", displayText); + assertEquals(displayText, cloneVpcOfferingCmd.getDisplayText()); + } + + @Test + public void testGetDisplayTextDefaultsToName() { + String name = "ClonedVpcOffering"; + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "vpcOfferingName", name); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "displayText", null); + assertEquals(name, cloneVpcOfferingCmd.getDisplayText()); + } + + @Test + public void testGetServiceOfferingId() { + Long serviceOfferingId = 456L; + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "serviceOfferingId", serviceOfferingId); + assertEquals(serviceOfferingId, cloneVpcOfferingCmd.getServiceOfferingId()); + } + + @Test + public void testGetInternetProtocol() { + String internetProtocol = "dualstack"; + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "internetProtocol", internetProtocol); + assertEquals(internetProtocol, cloneVpcOfferingCmd.getInternetProtocol()); + } + + @Test + public void testGetProvider() { + String provider = "NSX"; + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "provider", provider); + assertEquals(provider, cloneVpcOfferingCmd.getProvider()); + } + + @Test + public void testGetNetworkMode() { + String networkMode = "ROUTED"; + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "networkMode", networkMode); + assertEquals(networkMode, cloneVpcOfferingCmd.getNetworkMode()); + } + + @Test + public void testGetRoutingMode() { + String routingMode = "dynamic"; + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "routingMode", routingMode); + assertEquals(routingMode, cloneVpcOfferingCmd.getRoutingMode()); + } + + @Test + public void testGetNsxSupportLb() { + Boolean nsxSupportLb = true; + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "nsxSupportsLbService", nsxSupportLb); + assertEquals(nsxSupportLb, cloneVpcOfferingCmd.getNsxSupportsLbService()); + } + + @Test + public void testGetSpecifyAsnumber() { + Boolean specifyAsnumber = false; + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "specifyAsNumber", specifyAsnumber); + assertEquals(specifyAsnumber, cloneVpcOfferingCmd.getSpecifyAsNumber()); + } + + @Test + public void testExecuteSuccess() { + Long sourceOfferingId = 789L; + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "sourceOfferingId", sourceOfferingId); + + when(vpcService.cloneVPCOffering(any(CloneVPCOfferingCmd.class))).thenReturn(mockVpcOffering); + when(responseGenerator.createVpcOfferingResponse(mockVpcOffering)).thenReturn(mockVpcOfferingResponse); + + cloneVpcOfferingCmd.execute(); + + assertNotNull(cloneVpcOfferingCmd.getResponseObject()); + assertEquals(mockVpcOfferingResponse, cloneVpcOfferingCmd.getResponseObject()); + } + + @Test(expected = ServerApiException.class) + public void testExecuteFailure() { + Long sourceOfferingId = 789L; + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "sourceOfferingId", sourceOfferingId); + + when(vpcService.cloneVPCOffering(any(CloneVPCOfferingCmd.class))).thenReturn(null); + + try { + cloneVpcOfferingCmd.execute(); + fail("Expected ServerApiException to be thrown"); + } catch (ServerApiException e) { + assertEquals(ApiErrorCode.INTERNAL_ERROR, e.getErrorCode()); + assertEquals("Failed to clone VPC offering", e.getMessage()); + throw e; + } + } + + @Test + public void testGetSupportedServices() { + List supportedServices = Arrays.asList("Dhcp", "Dns", "SourceNat", "NetworkACL"); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "supportedServices", supportedServices); + assertEquals(supportedServices, cloneVpcOfferingCmd.getSupportedServices()); + } + + @Test + public void testGetServiceProviders() { + Map> serviceProviderList = new HashMap<>(); + + HashMap dhcpProvider = new HashMap<>(); + dhcpProvider.put("service", "Dhcp"); + dhcpProvider.put("provider", "VpcVirtualRouter"); + + HashMap dnsProvider = new HashMap<>(); + dnsProvider.put("service", "Dns"); + dnsProvider.put("provider", "VpcVirtualRouter"); + + HashMap aclProvider = new HashMap<>(); + aclProvider.put("service", "NetworkACL"); + aclProvider.put("provider", "VpcVirtualRouter"); + + serviceProviderList.put("0", dhcpProvider); + serviceProviderList.put("1", dnsProvider); + serviceProviderList.put("2", aclProvider); + + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "serviceProviderList", serviceProviderList); + + Map> result = cloneVpcOfferingCmd.getServiceProviders(); + assertNotNull(result); + assertEquals(3, result.size()); + assertNotNull(result.get("Dhcp")); + assertNotNull(result.get("Dns")); + assertNotNull(result.get("NetworkACL")); + assertEquals("VpcVirtualRouter", result.get("Dhcp").get(0)); + assertEquals("VpcVirtualRouter", result.get("Dns").get(0)); + assertEquals("VpcVirtualRouter", result.get("NetworkACL").get(0)); + } + + @Test + public void testGetEnable() { + Boolean enable = true; + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "enable", enable); + assertEquals(enable, cloneVpcOfferingCmd.getEnable()); + } + + @Test + public void testCloneWithAllParameters() { + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "sourceOfferingId", 789L); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "vpcOfferingName", "ClonedVpcOffering"); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "displayText", "Cloned VPC Offering"); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "serviceOfferingId", 456L); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "internetProtocol", "ipv4"); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "provider", "NSX"); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "networkMode", "NATTED"); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "routingMode", "static"); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "nsxSupportsLbService", true); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "specifyAsNumber", false); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "enable", true); + + assertEquals(Long.valueOf(789L), cloneVpcOfferingCmd.getSourceOfferingId()); + assertEquals("ClonedVpcOffering", cloneVpcOfferingCmd.getVpcOfferingName()); + assertEquals("Cloned VPC Offering", cloneVpcOfferingCmd.getDisplayText()); + assertEquals(Long.valueOf(456L), cloneVpcOfferingCmd.getServiceOfferingId()); + assertEquals("ipv4", cloneVpcOfferingCmd.getInternetProtocol()); + assertEquals("NSX", cloneVpcOfferingCmd.getProvider()); + assertEquals("NATTED", cloneVpcOfferingCmd.getNetworkMode()); + assertEquals("static", cloneVpcOfferingCmd.getRoutingMode()); + assertEquals(Boolean.TRUE, cloneVpcOfferingCmd.getNsxSupportsLbService()); + assertEquals(Boolean.FALSE, cloneVpcOfferingCmd.getSpecifyAsNumber()); + assertEquals(Boolean.TRUE, cloneVpcOfferingCmd.getEnable()); + } + + @Test + public void testSourceOfferingIdNullByDefault() { + assertNull(cloneVpcOfferingCmd.getSourceOfferingId()); + } + + @Test + public void testProviderNullByDefault() { + assertNull(cloneVpcOfferingCmd.getProvider()); + } + + @Test + public void testServiceCapabilityList() { + Map> serviceCapabilityList = new HashMap<>(); + serviceCapabilityList.put("Connectivity", Arrays.asList("RegionLevelVpc:true", "DistributedRouter:true")); + serviceCapabilityList.put("SourceNat", Arrays.asList("RedundantRouter:true")); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "serviceCapabilityList", serviceCapabilityList); + + Map> result = cloneVpcOfferingCmd.getServiceCapabilityList(); + assertNotNull(result); + assertEquals(serviceCapabilityList, result); + } + + @Test + public void testCloneVpcOfferingWithNsxProvider() { + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "sourceOfferingId", 789L); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "provider", "NSX"); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "nsxSupportsLbService", true); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "networkMode", "ROUTED"); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "routingMode", "dynamic"); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "specifyAsNumber", true); + + assertEquals("NSX", cloneVpcOfferingCmd.getProvider()); + assertEquals(Boolean.TRUE, cloneVpcOfferingCmd.getNsxSupportsLbService()); + assertEquals("ROUTED", cloneVpcOfferingCmd.getNetworkMode()); + assertEquals("dynamic", cloneVpcOfferingCmd.getRoutingMode()); + assertEquals(Boolean.TRUE, cloneVpcOfferingCmd.getSpecifyAsNumber()); + } + + @Test + public void testCloneVpcOfferingWithNetrisProvider() { + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "sourceOfferingId", 789L); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "provider", "Netris"); + ReflectionTestUtils.setField(cloneVpcOfferingCmd, "networkMode", "NATTED"); + + assertEquals("Netris", cloneVpcOfferingCmd.getProvider()); + assertEquals("NATTED", cloneVpcOfferingCmd.getNetworkMode()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/offering/DomainAndZoneIdResolverTest.java b/api/src/test/java/org/apache/cloudstack/api/command/offering/DomainAndZoneIdResolverTest.java new file mode 100644 index 000000000000..e679bbf2d1f1 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/offering/DomainAndZoneIdResolverTest.java @@ -0,0 +1,149 @@ +// 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.offering; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.LongFunction; + +import com.cloud.dc.DataCenter; +import com.cloud.domain.Domain; +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.utils.db.EntityManager; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.ServerApiException; +import org.junit.Assert; +import org.junit.Test; + +public class DomainAndZoneIdResolverTest { + static class TestCmd extends BaseCmd implements DomainAndZoneIdResolver { + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + // No implementation needed for tests + } + + @Override + public String getCommandName() { + return "test"; + } + + @Override + public long getEntityOwnerId() { + return 1L; + } + } + + private void setEntityMgr(final BaseCmd cmd, final EntityManager entityMgr) throws Exception { + Field f = BaseCmd.class.getDeclaredField("_entityMgr"); + f.setAccessible(true); + f.set(cmd, entityMgr); + } + + @Test + public void resolveDomainIds_usesDefaultProviderWhenEmpty() { + TestCmd cmd = new TestCmd(); + + final LongFunction> defaultsProvider = id -> Arrays.asList(100L, 200L); + + List result = cmd.resolveDomainIds("", 42L, defaultsProvider, "offering"); + Assert.assertEquals(Arrays.asList(100L, 200L), result); + } + + @Test + public void resolveDomainIds_resolvesValidUuids() throws Exception { + TestCmd cmd = new TestCmd(); + + EntityManager em = mock(EntityManager.class); + setEntityMgr(cmd, em); + + Domain d1 = mock(Domain.class); + when(d1.getId()).thenReturn(10L); + Domain d2 = mock(Domain.class); + when(d2.getId()).thenReturn(20L); + + when(em.findByUuid(Domain.class, "uuid1")).thenReturn(d1); + when(em.findByUuid(Domain.class, "uuid2")).thenReturn(d2); + + List ids = cmd.resolveDomainIds("uuid1, public, uuid2", null, null, "template"); + Assert.assertEquals(Arrays.asList(10L, 20L), ids); + } + + @Test + public void resolveDomainIds_invalidUuid_throws() throws Exception { + TestCmd cmd = new TestCmd(); + + EntityManager em = mock(EntityManager.class); + setEntityMgr(cmd, em); + + when(em.findByUuid(Domain.class, "bad-uuid")).thenReturn(null); + + Assert.assertThrows(InvalidParameterValueException.class, + () -> cmd.resolveDomainIds("bad-uuid", null, null, "offering")); + } + + @Test + public void resolveZoneIds_usesDefaultProviderWhenEmpty() { + TestCmd cmd = new TestCmd(); + + final LongFunction> defaultsProvider = id -> Collections.singletonList(300L); + + List result = cmd.resolveZoneIds("", 99L, defaultsProvider, "offering"); + Assert.assertEquals(Collections.singletonList(300L), result); + } + + @Test + public void resolveZoneIds_resolvesValidUuids() throws Exception { + TestCmd cmd = new TestCmd(); + + EntityManager em = mock(EntityManager.class); + setEntityMgr(cmd, em); + + DataCenter z1 = mock(DataCenter.class); + when(z1.getId()).thenReturn(30L); + DataCenter z2 = mock(DataCenter.class); + when(z2.getId()).thenReturn(40L); + + when(em.findByUuid(DataCenter.class, "zone-1")).thenReturn(z1); + when(em.findByUuid(DataCenter.class, "zone-2")).thenReturn(z2); + + List ids = cmd.resolveZoneIds("zone-1, all, zone-2", null, null, "service"); + Assert.assertEquals(Arrays.asList(30L, 40L), ids); + } + + @Test + public void resolveZoneIds_invalidUuid_throws() throws Exception { + TestCmd cmd = new TestCmd(); + + EntityManager em = mock(EntityManager.class); + setEntityMgr(cmd, em); + + when(em.findByUuid(DataCenter.class, "bad-zone")).thenReturn(null); + + Assert.assertThrows(InvalidParameterValueException.class, + () -> cmd.resolveZoneIds("bad-zone", null, null, "offering")); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/test/AddAccountToProjectCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/AddAccountToProjectCmdTest.java index f100822b8c77..cd0390aa2689 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/test/AddAccountToProjectCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/test/AddAccountToProjectCmdTest.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.test; +import com.cloud.exception.ResourceAllocationException; import junit.framework.Assert; import junit.framework.TestCase; @@ -149,6 +150,8 @@ public void testExecuteForNullAccountNameEmail() { addAccountToProjectCmd.execute(); } catch (InvalidParameterValueException exception) { Assert.assertEquals("Either accountName or email is required", exception.getLocalizedMessage()); + } catch (ResourceAllocationException exception) { + Assert.fail(); } } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/test/AddIpToVmNicTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/AddIpToVmNicTest.java index 9ea3a6446e8e..71c6d54c7a8c 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/test/AddIpToVmNicTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/test/AddIpToVmNicTest.java @@ -59,7 +59,7 @@ public void testCreateSuccess() throws ResourceAllocationException, ResourceUnav NicSecondaryIp secIp = Mockito.mock(NicSecondaryIp.class); Mockito.when( - networkService.allocateSecondaryGuestIP(ArgumentMatchers.anyLong(), ArgumentMatchers.any())) + networkService.allocateSecondaryGuestIP(ArgumentMatchers.anyLong(), ArgumentMatchers.any(), ArgumentMatchers.anyString())) .thenReturn(secIp); ipTonicCmd._networkService = networkService; @@ -79,7 +79,7 @@ public void testCreateFailure() throws ResourceAllocationException, ResourceUnav AddIpToVmNicCmd ipTonicCmd = Mockito.mock(AddIpToVmNicCmd.class); Mockito.when( - networkService.allocateSecondaryGuestIP(ArgumentMatchers.anyLong(), ArgumentMatchers.any())) + networkService.allocateSecondaryGuestIP(ArgumentMatchers.anyLong(), ArgumentMatchers.any(), ArgumentMatchers.anyString())) .thenReturn(null); ipTonicCmd._networkService = networkService; diff --git a/api/src/test/java/org/apache/cloudstack/api/command/test/AddVpnUserCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/AddVpnUserCmdTest.java index 8b933534cea2..89693f0d0675 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/test/AddVpnUserCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/test/AddVpnUserCmdTest.java @@ -111,7 +111,7 @@ public void testCreateFailure() { try { addVpnUserCmd.create(); } catch (ServerApiException exception) { - Assert.assertEquals("Failed to add vpn user", exception.getDescription()); + Assert.assertEquals("Failed to add VPN User", exception.getDescription()); } } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/test/CreateAutoScaleVmProfileCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/CreateAutoScaleVmProfileCmdTest.java index b459b1a358cc..8e48cb16a3b4 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/test/CreateAutoScaleVmProfileCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/test/CreateAutoScaleVmProfileCmdTest.java @@ -122,7 +122,7 @@ public void verifyCreateAutoScaleVmProfileCmd() { Assert.assertEquals("autoscalevmprofileresponse", createAutoScaleVmProfileCmd.getCommandName()); Assert.assertEquals("autoscalevmprofile", CreateAutoScaleVmProfileCmd.getResultObjectName()); Assert.assertEquals(EventTypes.EVENT_AUTOSCALEVMPROFILE_CREATE, createAutoScaleVmProfileCmd.getEventType()); - Assert.assertEquals("creating AutoScale Vm Profile", createAutoScaleVmProfileCmd.getEventDescription()); + Assert.assertEquals("Creating AutoScale Instance Profile", createAutoScaleVmProfileCmd.getEventDescription()); Assert.assertEquals(ApiCommandResourceType.AutoScaleVmProfile, createAutoScaleVmProfileCmd.getApiResourceType()); } 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 5fa46ec97e52..032dca8d8007 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 @@ -25,11 +25,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; import org.apache.cloudstack.api.ResponseGenerator; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.command.user.snapshot.CreateSnapshotCmd; import org.apache.cloudstack.api.response.SnapshotResponse; +import org.apache.cloudstack.context.CallContext; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -73,11 +75,6 @@ public Long getVolumeId(){ public long getEntityOwnerId(){ return 1L; } - - @Override - protected String getVolumeUuid() { - return "123"; - } }; } @@ -121,6 +118,9 @@ public void testCreateFailure() { AccountService accountService = Mockito.mock(AccountService.class); Account account = Mockito.mock(Account.class); Mockito.when(accountService.getAccount(anyLong())).thenReturn(account); + String volumeUuid = UUID.randomUUID().toString(); + + CallContext.current().putApiResourceUuid("volumeid", volumeUuid); VolumeApiService volumeApiService = Mockito.mock(VolumeApiService.class); @@ -137,7 +137,7 @@ public void testCreateFailure() { try { createSnapshotCmd.execute(); } catch (ServerApiException exception) { - Assert.assertEquals("Failed to create snapshot due to an internal error creating snapshot for volume 123", exception.getDescription()); + Assert.assertEquals("Failed to create Snapshot due to an internal error creating Snapshot for volume " + volumeUuid, exception.getDescription()); } } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/test/ScaleVMCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/ScaleVMCmdTest.java index 3ed1d9389d42..1150c40ba488 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/test/ScaleVMCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/test/ScaleVMCmdTest.java @@ -78,10 +78,6 @@ public void testCreateSuccess() { scaleVMCmd._responseGenerator = responseGenerator; UserVmResponse userVmResponse = Mockito.mock(UserVmResponse.class); - //List list = Mockito.mock(UserVmResponse.class); - //list.add(userVmResponse); - //LinkedList mockedList = Mockito.mock(LinkedList.class); - //Mockito.when(mockedList.get(0)).thenReturn(userVmResponse); List list = new LinkedList(); list.add(userVmResponse); @@ -111,7 +107,7 @@ public void testCreateFailure() { try { scaleVMCmd.execute(); } catch (ServerApiException exception) { - Assert.assertEquals("Failed to scale vm", exception.getDescription()); + Assert.assertEquals("Failed to scale Instance", exception.getDescription()); } } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateAutoScaleVmProfileCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateAutoScaleVmProfileCmdTest.java index bf387e8216e8..3409ce053a9f 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateAutoScaleVmProfileCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateAutoScaleVmProfileCmdTest.java @@ -100,7 +100,7 @@ public void verifyUpdateAutoScaleVmProfileCmd() { Assert.assertEquals("updateautoscalevmprofileresponse", updateAutoScaleVmProfileCmd.getCommandName()); Assert.assertEquals(EventTypes.EVENT_AUTOSCALEVMPROFILE_UPDATE, updateAutoScaleVmProfileCmd.getEventType()); - Assert.assertEquals("Updating AutoScale Vm Profile. Vm Profile Id: " + profileId, updateAutoScaleVmProfileCmd.getEventDescription()); + Assert.assertEquals("Updating AutoScale Instance Profile with ID: " + profileId, updateAutoScaleVmProfileCmd.getEventDescription()); } @Test diff --git a/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateConditionCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateConditionCmdTest.java index 7d6f8dc35b7e..c78dbe9b56bc 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateConditionCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateConditionCmdTest.java @@ -31,12 +31,15 @@ import org.apache.cloudstack.api.command.user.autoscale.UpdateConditionCmd; import org.apache.cloudstack.api.response.ConditionResponse; +import org.apache.cloudstack.context.CallContext; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.springframework.test.util.ReflectionTestUtils; +import java.util.UUID; + import static org.mockito.Mockito.when; public class UpdateConditionCmdTest { @@ -53,6 +56,7 @@ public class UpdateConditionCmdTest { private static final Long threshold = 100L; private static final long accountId = 5L; + private static final String conditionUuid = UUID.randomUUID().toString(); @Before public void setUp() { @@ -71,6 +75,8 @@ public void setUp() { ReflectionTestUtils.setField(updateConditionCmd,"relationalOperator", relationalOperator); ReflectionTestUtils.setField(updateConditionCmd,"threshold", threshold); + CallContext.current().putApiResourceUuid("id", conditionUuid); + condition = Mockito.mock(Condition.class); } @@ -83,7 +89,7 @@ public void verifyUpdateConditionCmd() { Assert.assertEquals(ApiCommandResourceType.Condition, updateConditionCmd.getApiResourceType()); Assert.assertEquals("updateconditionresponse", updateConditionCmd.getCommandName()); Assert.assertEquals(EventTypes.EVENT_CONDITION_UPDATE, updateConditionCmd.getEventType()); - Assert.assertEquals("Updating a condition.", updateConditionCmd.getEventDescription()); + Assert.assertEquals("Updating Instance AutoScale condition with ID: " + conditionUuid, updateConditionCmd.getEventDescription()); when(entityMgr.findById(Condition.class, conditionId)).thenReturn(condition); when(condition.getAccountId()).thenReturn(accountId); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateVmNicIpTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateVmNicIpTest.java index 9a42aa1fbc00..8727bab6f105 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateVmNicIpTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateVmNicIpTest.java @@ -84,7 +84,7 @@ public void testFailure() throws ResourceAllocationException, ResourceUnavailabl try { updateVmNicIpCmd.execute(); } catch (ServerApiException exception) { - Assert.assertEquals("Failed to update ip address on vm NIC. Refer to server logs for details.", exception.getDescription()); + Assert.assertEquals("Failed to update IP address on Instance NIC. Refer to server logs for details.", exception.getDescription()); } } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/backup/ListBackupScheduleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/backup/ListBackupScheduleCmdTest.java new file mode 100644 index 000000000000..a0d88bbc84e9 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/backup/ListBackupScheduleCmdTest.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.api.command.user.backup; + +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.ResponseGenerator; +import org.apache.cloudstack.api.response.BackupScheduleResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.backup.BackupManager; +import org.apache.cloudstack.backup.BackupSchedule; +import org.apache.cloudstack.context.CallContext; +import org.junit.Assert; +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; + +@RunWith(MockitoJUnitRunner.class) +public class ListBackupScheduleCmdTest { + + @Mock + private BackupManager backupManager; + + @Mock + private ResponseGenerator responseGenerator; + + private ListBackupScheduleCmd cmd; + + @Before + public void setUp() { + cmd = new ListBackupScheduleCmd(); + cmd.backupManager = backupManager; + cmd._responseGenerator = responseGenerator; + } + + @Test + public void testExecuteWithSchedules() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, NetworkRuleConflictException { + BackupSchedule schedule = Mockito.mock(BackupSchedule.class); + BackupScheduleResponse scheduleResponse = Mockito.mock(BackupScheduleResponse.class); + List schedules = new ArrayList<>(); + schedules.add(schedule); + + Mockito.when(backupManager.listBackupSchedules(cmd)).thenReturn(schedules); + Mockito.when(responseGenerator.createBackupScheduleResponse(schedule)).thenReturn(scheduleResponse); + + Account mockAccount = Mockito.mock(Account.class); + CallContext callContext = Mockito.mock(CallContext.class); + try (org.mockito.MockedStatic mocked = Mockito.mockStatic(CallContext.class)) { + cmd.execute(); + } + + ListResponse response = (ListResponse) cmd.getResponseObject(); + Assert.assertNotNull(response); + Assert.assertEquals(1, response.getResponses().size()); + Assert.assertEquals(scheduleResponse, response.getResponses().get(0)); + } + + @Test + public void testExecuteWithNoSchedules() { + Mockito.when(backupManager.listBackupSchedules(cmd)).thenReturn(new ArrayList<>()); + CallContext callContext = Mockito.mock(CallContext.class); + + try (org.mockito.MockedStatic mocked = Mockito.mockStatic(CallContext.class)) { + mocked.when(CallContext::current).thenReturn(callContext); + cmd.execute(); + } catch (ResourceUnavailableException | InsufficientCapacityException | ResourceAllocationException | + NetworkRuleConflictException e) { + throw new RuntimeException(e); + } + + ListResponse response = (ListResponse) cmd.getResponseObject(); + Assert.assertNotNull(response); + Assert.assertEquals(0, response.getResponses().size()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/dns/AddDnsServerCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/AddDnsServerCmdTest.java new file mode 100644 index 000000000000..2824a8dacb94 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/AddDnsServerCmdTest.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.api.command.user.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; + +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DnsServerResponse; +import org.apache.cloudstack.dns.DnsProviderType; +import org.apache.cloudstack.dns.DnsServer; +import org.junit.Test; + +public class AddDnsServerCmdTest extends BaseDnsCmdTest { + + private AddDnsServerCmd createCmd() throws Exception { + AddDnsServerCmd cmd = new AddDnsServerCmd(); + setField(cmd, "dnsProviderManager", dnsProviderManager); + setField(cmd, "name", "test-dns"); + setField(cmd, "url", "http://dns.example.com"); + setField(cmd, "provider", "PowerDNS"); + setField(cmd, "dnsApiKey", "api-key-123"); + setField(cmd, "port", 8081); + setField(cmd, "isPublic", true); + setField(cmd, "publicDomainSuffix", "public.example.com"); + setField(cmd, "nameServers", Arrays.asList("ns1.example.com", "ns2.example.com")); + setField(cmd, "dnsUserName", "admin@example.com"); + return cmd; + } + + @Test + public void testAccessors() throws Exception { + AddDnsServerCmd cmd = createCmd(); + + assertEquals("test-dns", cmd.getName()); + assertEquals("http://dns.example.com", cmd.getUrl()); + assertEquals("api-key-123", cmd.getDnsApiKey()); + assertEquals(Integer.valueOf(8081), cmd.getPort()); + assertTrue(cmd.isPublic()); + assertEquals("public.example.com", cmd.getPublicDomainSuffix()); + assertEquals(Arrays.asList("ns1.example.com", "ns2.example.com"), cmd.getNameServers()); + assertEquals(DnsProviderType.PowerDNS, cmd.getProvider()); + assertEquals("admin@example.com", cmd.getDnsUserName()); + } + + @Test + public void testIsPublicFalse() throws Exception { + AddDnsServerCmd cmd = createCmd(); + setField(cmd, "isPublic", false); + assertFalse(cmd.isPublic()); + } + + @Test + public void testIsPublicNull() throws Exception { + AddDnsServerCmd cmd = createCmd(); + setField(cmd, "isPublic", null); + assertFalse(cmd.isPublic()); + } + + @Test + public void testGetEntityOwnerId() throws Exception { + AddDnsServerCmd cmd = createCmd(); + assertEquals(ACCOUNT_ID, cmd.getEntityOwnerId()); + } + + @Test + public void testGetProviderDefault() throws Exception { + AddDnsServerCmd cmd = createCmd(); + setField(cmd, "provider", null); + assertEquals(DnsProviderType.PowerDNS, cmd.getProvider()); + } + + @Test + public void testGetProviderCaseInsensitive() throws Exception { + AddDnsServerCmd cmd = createCmd(); + setField(cmd, "provider", "powerdns"); + assertEquals(DnsProviderType.PowerDNS, cmd.getProvider()); + } + + @Test + public void testExecuteSuccess() throws Exception { + AddDnsServerCmd cmd = createCmd(); + + DnsServer mockServer = mock(DnsServer.class); + DnsServerResponse mockResponse = new DnsServerResponse(); + mockResponse.setName("test-dns"); + + when(dnsProviderManager.addDnsServer(cmd)).thenReturn(mockServer); + when(dnsProviderManager.createDnsServerResponse(mockServer)).thenReturn(mockResponse); + + cmd.execute(); + + DnsServerResponse response = (DnsServerResponse) cmd.getResponseObject(); + assertNotNull(response); + assertEquals("adddnsserverresponse", response.getResponseName()); + verify(dnsProviderManager).addDnsServer(cmd); + verify(dnsProviderManager).createDnsServerResponse(mockServer); + } + + @Test(expected = ServerApiException.class) + public void testExecuteReturnsNull() throws Exception { + AddDnsServerCmd cmd = createCmd(); + when(dnsProviderManager.addDnsServer(cmd)).thenReturn(null); + cmd.execute(); + } + + @Test(expected = ServerApiException.class) + public void testExecuteThrowsException() throws Exception { + AddDnsServerCmd cmd = createCmd(); + when(dnsProviderManager.addDnsServer(cmd)).thenThrow(new RuntimeException("Connection refused")); + cmd.execute(); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/dns/AssociateDnsZoneToNetworkCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/AssociateDnsZoneToNetworkCmdTest.java new file mode 100644 index 000000000000..593a0a041cea --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/AssociateDnsZoneToNetworkCmdTest.java @@ -0,0 +1,97 @@ +// 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.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DnsZoneNetworkMapResponse; +import org.junit.Test; + +import com.cloud.network.Network; +import com.cloud.user.Account; + +public class AssociateDnsZoneToNetworkCmdTest extends BaseDnsCmdTest { + + private static final long NETWORK_ID = 200L; + + private AssociateDnsZoneToNetworkCmd createCmd() throws Exception { + AssociateDnsZoneToNetworkCmd cmd = new AssociateDnsZoneToNetworkCmd(); + setField(cmd, "dnsProviderManager", dnsProviderManager); + setField(cmd, "_entityMgr", entityManager); + setField(cmd, "dnsZoneId", ENTITY_ID); + setField(cmd, "networkId", NETWORK_ID); + setField(cmd, "subDomain", "dev"); + return cmd; + } + + @Test + public void testAccessors() throws Exception { + AssociateDnsZoneToNetworkCmd cmd = createCmd(); + assertEquals(Long.valueOf(ENTITY_ID), cmd.getDnsZoneId()); + assertEquals(Long.valueOf(NETWORK_ID), cmd.getNetworkId()); + assertEquals("dev", cmd.getSubDomain()); + } + + @Test + public void testGetEntityOwnerIdWithNetwork() throws Exception { + AssociateDnsZoneToNetworkCmd cmd = createCmd(); + Network mockNetwork = mock(Network.class); + when(mockNetwork.getAccountId()).thenReturn(ACCOUNT_ID); + when(entityManager.findById(Network.class, NETWORK_ID)) + .thenReturn(mockNetwork); + + assertEquals(ACCOUNT_ID, cmd.getEntityOwnerId()); + } + + @Test + public void testGetEntityOwnerIdNetworkNotFound() throws Exception { + AssociateDnsZoneToNetworkCmd cmd = createCmd(); + when(entityManager.findById(Network.class, NETWORK_ID)) + .thenReturn(null); + + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } + + @Test + public void testExecuteSuccess() throws Exception { + AssociateDnsZoneToNetworkCmd cmd = createCmd(); + DnsZoneNetworkMapResponse mockResponse = + new DnsZoneNetworkMapResponse(); + when(dnsProviderManager.associateZoneToNetwork(cmd)) + .thenReturn(mockResponse); + + cmd.execute(); + + DnsZoneNetworkMapResponse response = + (DnsZoneNetworkMapResponse) cmd.getResponseObject(); + assertNotNull(response); + assertEquals("associatednszonetonetworkresponse", + response.getResponseName()); + } + + @Test(expected = ServerApiException.class) + public void testExecuteThrowsException() throws Exception { + AssociateDnsZoneToNetworkCmd cmd = createCmd(); + when(dnsProviderManager.associateZoneToNetwork(cmd)) + .thenThrow(new RuntimeException("Error")); + cmd.execute(); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/dns/BaseDnsCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/BaseDnsCmdTest.java new file mode 100644 index 000000000000..e8b611ca17ca --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/BaseDnsCmdTest.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.api.command.user.dns; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Field; + +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.dns.DnsProviderManager; +import org.junit.After; +import org.junit.Before; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import com.cloud.user.Account; +import com.cloud.utils.db.EntityManager; + +/** + * Shared setup for all DNS command unit tests. + */ +public abstract class BaseDnsCmdTest { + + protected static final long ACCOUNT_ID = 42L; + protected static final long ENTITY_ID = 100L; + + protected DnsProviderManager dnsProviderManager; + protected EntityManager entityManager; + protected Account callingAccount; + + private MockedStatic callContextMock; + + @Before + public void setUp() { + dnsProviderManager = mock(DnsProviderManager.class); + entityManager = mock(EntityManager.class); + + callingAccount = mock(Account.class); + when(callingAccount.getId()).thenReturn(ACCOUNT_ID); + + CallContext callContext = mock(CallContext.class); + when(callContext.getCallingAccount()).thenReturn(callingAccount); + + callContextMock = Mockito.mockStatic(CallContext.class); + callContextMock.when(CallContext::current).thenReturn(callContext); + } + + @After + public void tearDown() { + callContextMock.close(); + } + + /** + * Sets a private/inherited field value via reflection. + */ + protected void setField(Object target, String fieldName, Object value) throws Exception { + Field field = null; + Class clazz = target.getClass(); + while (clazz != null) { + try { + field = clazz.getDeclaredField(fieldName); + break; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + if (field == null) { + throw new NoSuchFieldException(fieldName + " not found in hierarchy of " + target.getClass().getName()); + } + field.setAccessible(true); + field.set(target, value); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/dns/CreateDnsRecordCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/CreateDnsRecordCmdTest.java new file mode 100644 index 000000000000..c37930dd2289 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/CreateDnsRecordCmdTest.java @@ -0,0 +1,117 @@ +// 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.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.when; + +import java.util.Arrays; + +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DnsRecordResponse; +import org.apache.cloudstack.dns.DnsRecord; +import org.junit.Test; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; + +public class CreateDnsRecordCmdTest extends BaseDnsCmdTest { + + private CreateDnsRecordCmd createCmd() throws Exception { + CreateDnsRecordCmd cmd = new CreateDnsRecordCmd(); + setField(cmd, "dnsProviderManager", dnsProviderManager); + setField(cmd, "dnsZoneId", ENTITY_ID); + setField(cmd, "name", "www"); + setField(cmd, "type", "A"); + setField(cmd, "contents", Arrays.asList("192.168.1.1")); + setField(cmd, "ttl", 7200); + return cmd; + } + + @Test + public void testAccessors() throws Exception { + CreateDnsRecordCmd cmd = createCmd(); + + assertEquals(Long.valueOf(ENTITY_ID), cmd.getDnsZoneId()); + assertEquals("www", cmd.getName()); + assertEquals(DnsRecord.RecordType.A, cmd.getType()); + assertEquals(Arrays.asList("192.168.1.1"), cmd.getContents()); + assertEquals(Integer.valueOf(7200), cmd.getTtl()); + } + + @Test + public void testGetTtlDefault() throws Exception { + CreateDnsRecordCmd cmd = createCmd(); + setField(cmd, "ttl", null); + assertEquals(Integer.valueOf(3600), cmd.getTtl()); + } + + @Test + public void testGetTypeCname() throws Exception { + CreateDnsRecordCmd cmd = createCmd(); + setField(cmd, "type", "CNAME"); + assertEquals(DnsRecord.RecordType.CNAME, cmd.getType()); + } + + @Test(expected = InvalidParameterValueException.class) + public void testGetTypeInvalid() throws Exception { + CreateDnsRecordCmd cmd = createCmd(); + setField(cmd, "type", "INVALID"); + cmd.getType(); + } + + @Test + public void testGetEntityOwnerId() throws Exception { + CreateDnsRecordCmd cmd = createCmd(); + assertEquals(ACCOUNT_ID, cmd.getEntityOwnerId()); + } + + @Test + public void testEventType() throws Exception { + CreateDnsRecordCmd cmd = createCmd(); + assertEquals(EventTypes.EVENT_DNS_RECORD_CREATE, cmd.getEventType()); + } + + @Test + public void testEventDescription() throws Exception { + CreateDnsRecordCmd cmd = createCmd(); + assertEquals("Creating DNS Record: www", cmd.getEventDescription()); + } + + @Test + public void testExecuteSuccess() throws Exception { + CreateDnsRecordCmd cmd = createCmd(); + + DnsRecordResponse mockResponse = new DnsRecordResponse(); + mockResponse.setName("www"); + when(dnsProviderManager.createDnsRecord(cmd)).thenReturn(mockResponse); + + cmd.execute(); + + DnsRecordResponse response = (DnsRecordResponse) cmd.getResponseObject(); + assertNotNull(response); + assertEquals("creatednsrecordresponse", response.getResponseName()); + } + + @Test(expected = ServerApiException.class) + public void testExecuteThrowsException() throws Exception { + CreateDnsRecordCmd cmd = createCmd(); + when(dnsProviderManager.createDnsRecord(cmd)).thenThrow(new RuntimeException("Provider error")); + cmd.execute(); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/dns/CreateDnsZoneCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/CreateDnsZoneCmdTest.java new file mode 100644 index 000000000000..dc37874e6599 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/CreateDnsZoneCmdTest.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.api.command.user.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DnsZoneResponse; +import org.apache.cloudstack.dns.DnsZone; +import org.junit.Test; + +import com.cloud.event.EventTypes; + +public class CreateDnsZoneCmdTest extends BaseDnsCmdTest { + + private CreateDnsZoneCmd createCmd() throws Exception { + CreateDnsZoneCmd cmd = new CreateDnsZoneCmd(); + setField(cmd, "dnsProviderManager", dnsProviderManager); + setField(cmd, "name", "example.com"); + setField(cmd, "dnsServerId", ENTITY_ID); + setField(cmd, "type", "Public"); + setField(cmd, "description", "Test zone"); + return cmd; + } + + @Test + public void testAccessors() throws Exception { + CreateDnsZoneCmd cmd = createCmd(); + + assertEquals("example.com", cmd.getName()); + assertEquals(Long.valueOf(ENTITY_ID), cmd.getDnsServerId()); + assertEquals(DnsZone.ZoneType.Public, cmd.getType()); + assertEquals("Test zone", cmd.getDescription()); + } + + @Test + public void testGetTypePrivate() throws Exception { + CreateDnsZoneCmd cmd = createCmd(); + setField(cmd, "type", "Private"); + assertEquals(DnsZone.ZoneType.Private, cmd.getType()); + } + + @Test + public void testGetTypeDefaultsToPublicWhenNull() throws Exception { + CreateDnsZoneCmd cmd = createCmd(); + setField(cmd, "type", null); + assertEquals(DnsZone.ZoneType.Public, cmd.getType()); + } + + @Test + public void testGetTypeDefaultsToPublicWhenBlank() throws Exception { + CreateDnsZoneCmd cmd = createCmd(); + setField(cmd, "type", ""); + assertEquals(DnsZone.ZoneType.Public, cmd.getType()); + } + + @Test + public void testGetEntityOwnerId() throws Exception { + CreateDnsZoneCmd cmd = createCmd(); + assertEquals(ACCOUNT_ID, cmd.getEntityOwnerId()); + } + + @Test + public void testEventType() throws Exception { + CreateDnsZoneCmd cmd = createCmd(); + assertEquals(EventTypes.EVENT_DNS_ZONE_CREATE, cmd.getEventType()); + } + + @Test + public void testEventDescription() throws Exception { + CreateDnsZoneCmd cmd = createCmd(); + assertEquals("creating DNS zone: example.com", cmd.getEventDescription()); + } + + @Test + public void testCreateSuccess() throws Exception { + CreateDnsZoneCmd cmd = createCmd(); + + DnsZone mockZone = mock(DnsZone.class); + when(mockZone.getId()).thenReturn(ENTITY_ID); + when(mockZone.getUuid()).thenReturn("uuid-123"); + when(dnsProviderManager.allocateDnsZone(cmd)).thenReturn(mockZone); + + cmd.create(); + + assertEquals(Long.valueOf(ENTITY_ID), cmd.getEntityId()); + assertEquals("uuid-123", cmd.getEntityUuid()); + } + + @Test(expected = ServerApiException.class) + public void testCreateReturnsNull() throws Exception { + CreateDnsZoneCmd cmd = createCmd(); + when(dnsProviderManager.allocateDnsZone(cmd)).thenReturn(null); + cmd.create(); + } + + @Test(expected = ServerApiException.class) + public void testCreateThrowsException() throws Exception { + CreateDnsZoneCmd cmd = createCmd(); + when(dnsProviderManager.allocateDnsZone(cmd)).thenThrow(new RuntimeException("DB error")); + cmd.create(); + } + + @Test + public void testExecuteSuccess() throws Exception { + CreateDnsZoneCmd cmd = createCmd(); + cmd.setEntityId(ENTITY_ID); + + DnsZone mockZone = mock(DnsZone.class); + DnsZoneResponse mockResponse = new DnsZoneResponse(); + mockResponse.setName("example.com"); + + when(dnsProviderManager.provisionDnsZone(ENTITY_ID, false)).thenReturn(mockZone); + when(dnsProviderManager.createDnsZoneResponse(mockZone)).thenReturn(mockResponse); + + cmd.execute(); + + DnsZoneResponse response = (DnsZoneResponse) cmd.getResponseObject(); + assertNotNull(response); + assertEquals("creatednszoneresponse", response.getResponseName()); + } + + @Test(expected = ServerApiException.class) + public void testExecuteReturnsNull() throws Exception { + CreateDnsZoneCmd cmd = createCmd(); + cmd.setEntityId(ENTITY_ID); + + when(dnsProviderManager.provisionDnsZone(ENTITY_ID, false)).thenReturn(null); + cmd.execute(); + } + + @Test(expected = ServerApiException.class) + public void testExecuteThrowsException() throws Exception { + CreateDnsZoneCmd cmd = createCmd(); + cmd.setEntityId(ENTITY_ID); + + when(dnsProviderManager.provisionDnsZone(ENTITY_ID, false)).thenThrow(new RuntimeException("Provider error")); + cmd.execute(); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsRecordCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsRecordCmdTest.java new file mode 100644 index 000000000000..46aacf5a68ce --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsRecordCmdTest.java @@ -0,0 +1,102 @@ +// 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.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.dns.DnsRecord; +import org.junit.Test; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; + +public class DeleteDnsRecordCmdTest extends BaseDnsCmdTest { + + private DeleteDnsRecordCmd createCmd() throws Exception { + DeleteDnsRecordCmd cmd = new DeleteDnsRecordCmd(); + setField(cmd, "dnsProviderManager", dnsProviderManager); + setField(cmd, "dnsZoneId", ENTITY_ID); + setField(cmd, "name", "www"); + setField(cmd, "type", "A"); + return cmd; + } + + @Test + public void testAccessors() throws Exception { + DeleteDnsRecordCmd cmd = createCmd(); + + assertEquals(Long.valueOf(ENTITY_ID), cmd.getDnsZoneId()); + assertEquals("www", cmd.getName()); + assertEquals(DnsRecord.RecordType.A, cmd.getType()); + } + + @Test(expected = InvalidParameterValueException.class) + public void testGetTypeInvalid() throws Exception { + DeleteDnsRecordCmd cmd = createCmd(); + setField(cmd, "type", "BOGUS"); + cmd.getType(); + } + + @Test + public void testGetEntityOwnerId() throws Exception { + DeleteDnsRecordCmd cmd = createCmd(); + assertEquals(ACCOUNT_ID, cmd.getEntityOwnerId()); + } + + @Test + public void testEventType() throws Exception { + DeleteDnsRecordCmd cmd = createCmd(); + assertEquals(EventTypes.EVENT_DNS_RECORD_DELETE, cmd.getEventType()); + } + + @Test + public void testEventDescription() throws Exception { + DeleteDnsRecordCmd cmd = createCmd(); + assertEquals("Deleting DNS Record: www", cmd.getEventDescription()); + } + + @Test + public void testExecuteSuccess() throws Exception { + DeleteDnsRecordCmd cmd = createCmd(); + when(dnsProviderManager.deleteDnsRecord(cmd)).thenReturn(true); + + cmd.execute(); + + SuccessResponse response = (SuccessResponse) cmd.getResponseObject(); + assertNotNull(response); + verify(dnsProviderManager).deleteDnsRecord(cmd); + } + + @Test(expected = ServerApiException.class) + public void testExecuteReturnsFalse() throws Exception { + DeleteDnsRecordCmd cmd = createCmd(); + when(dnsProviderManager.deleteDnsRecord(cmd)).thenReturn(false); + cmd.execute(); + } + + @Test(expected = ServerApiException.class) + public void testExecuteThrowsException() throws Exception { + DeleteDnsRecordCmd cmd = createCmd(); + when(dnsProviderManager.deleteDnsRecord(cmd)).thenThrow(new RuntimeException("Error")); + cmd.execute(); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsServerCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsServerCmdTest.java new file mode 100644 index 000000000000..adebd339d710 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsServerCmdTest.java @@ -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. +package org.apache.cloudstack.api.command.user.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.dns.DnsServer; +import org.junit.Test; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; + +public class DeleteDnsServerCmdTest extends BaseDnsCmdTest { + + private DeleteDnsServerCmd createCmd() throws Exception { + DeleteDnsServerCmd cmd = new DeleteDnsServerCmd(); + setField(cmd, "dnsProviderManager", dnsProviderManager); + setField(cmd, "_entityMgr", entityManager); + setField(cmd, "id", ENTITY_ID); + return cmd; + } + + @Test + public void testGetId() throws Exception { + DeleteDnsServerCmd cmd = createCmd(); + assertEquals(Long.valueOf(ENTITY_ID), cmd.getId()); + } + + @Test + public void testGetCleanupDefault() throws Exception { + DeleteDnsServerCmd cmd = createCmd(); + assertTrue(cmd.getCleanup()); + } + + @Test + public void testGetCleanupFalse() throws Exception { + DeleteDnsServerCmd cmd = createCmd(); + setField(cmd, "cleanup", false); + assertFalse(cmd.getCleanup()); + } + + @Test + public void testGetEntityOwnerIdWithServer() throws Exception { + DeleteDnsServerCmd cmd = createCmd(); + DnsServer mockServer = mock(DnsServer.class); + when(mockServer.getAccountId()).thenReturn(ACCOUNT_ID); + when(entityManager.findById(DnsServer.class, ENTITY_ID)).thenReturn(mockServer); + + assertEquals(ACCOUNT_ID, cmd.getEntityOwnerId()); + } + + @Test + public void testGetEntityOwnerIdServerNotFound() throws Exception { + DeleteDnsServerCmd cmd = createCmd(); + when(entityManager.findById(DnsServer.class, ENTITY_ID)).thenReturn(null); + + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } + + @Test + public void testEventType() throws Exception { + DeleteDnsServerCmd cmd = createCmd(); + assertEquals(EventTypes.EVENT_DNS_SERVER_DELETE, cmd.getEventType()); + } + + @Test + public void testEventDescription() throws Exception { + DeleteDnsServerCmd cmd = createCmd(); + assertEquals("Deleting DNS server ID: " + ENTITY_ID, cmd.getEventDescription()); + } + + @Test + public void testExecuteSuccess() throws Exception { + DeleteDnsServerCmd cmd = createCmd(); + when(dnsProviderManager.deleteDnsServer(cmd)).thenReturn(true); + + cmd.execute(); + + SuccessResponse response = (SuccessResponse) cmd.getResponseObject(); + assertNotNull(response); + verify(dnsProviderManager).deleteDnsServer(cmd); + } + + @Test(expected = ServerApiException.class) + public void testExecuteReturnsFalse() throws Exception { + DeleteDnsServerCmd cmd = createCmd(); + when(dnsProviderManager.deleteDnsServer(cmd)).thenReturn(false); + cmd.execute(); + } + + @Test(expected = ServerApiException.class) + public void testExecuteThrowsException() throws Exception { + DeleteDnsServerCmd cmd = createCmd(); + when(dnsProviderManager.deleteDnsServer(cmd)).thenThrow(new RuntimeException("Error")); + cmd.execute(); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsZoneCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsZoneCmdTest.java new file mode 100644 index 000000000000..350cea76e5c9 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsZoneCmdTest.java @@ -0,0 +1,104 @@ +// 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.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.dns.DnsZone; +import org.junit.Test; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; + +public class DeleteDnsZoneCmdTest extends BaseDnsCmdTest { + + private DeleteDnsZoneCmd createCmd() throws Exception { + DeleteDnsZoneCmd cmd = new DeleteDnsZoneCmd(); + setField(cmd, "dnsProviderManager", dnsProviderManager); + setField(cmd, "_entityMgr", entityManager); + setField(cmd, "id", ENTITY_ID); + return cmd; + } + + @Test + public void testGetId() throws Exception { + DeleteDnsZoneCmd cmd = createCmd(); + assertEquals(Long.valueOf(ENTITY_ID), cmd.getId()); + } + + @Test + public void testGetEntityOwnerIdWithZone() throws Exception { + DeleteDnsZoneCmd cmd = createCmd(); + DnsZone mockZone = mock(DnsZone.class); + when(mockZone.getAccountId()).thenReturn(ACCOUNT_ID); + when(entityManager.findById(DnsZone.class, ENTITY_ID)).thenReturn(mockZone); + + assertEquals(ACCOUNT_ID, cmd.getEntityOwnerId()); + } + + @Test + public void testGetEntityOwnerIdZoneNotFound() throws Exception { + DeleteDnsZoneCmd cmd = createCmd(); + when(entityManager.findById(DnsZone.class, ENTITY_ID)).thenReturn(null); + + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } + + @Test + public void testEventType() throws Exception { + DeleteDnsZoneCmd cmd = createCmd(); + assertEquals(EventTypes.EVENT_DNS_ZONE_DELETE, cmd.getEventType()); + } + + @Test + public void testEventDescription() throws Exception { + DeleteDnsZoneCmd cmd = createCmd(); + assertEquals("Deleting DNS Zone ID: " + ENTITY_ID, cmd.getEventDescription()); + } + + @Test + public void testExecuteSuccess() throws Exception { + DeleteDnsZoneCmd cmd = createCmd(); + when(dnsProviderManager.deleteDnsZone(ENTITY_ID, false)).thenReturn(true); + + cmd.execute(); + + SuccessResponse response = (SuccessResponse) cmd.getResponseObject(); + assertNotNull(response); + verify(dnsProviderManager).deleteDnsZone(ENTITY_ID, false); + } + + @Test(expected = ServerApiException.class) + public void testExecuteReturnsFalse() throws Exception { + DeleteDnsZoneCmd cmd = createCmd(); + when(dnsProviderManager.deleteDnsZone(ENTITY_ID, false)).thenReturn(false); + cmd.execute(); + } + + @Test(expected = ServerApiException.class) + public void testExecuteThrowsException() throws Exception { + DeleteDnsZoneCmd cmd = createCmd(); + when(dnsProviderManager.deleteDnsZone(ENTITY_ID, false)).thenThrow(new RuntimeException("Error")); + cmd.execute(); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/dns/DisassociateDnsZoneFromNetworkCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/DisassociateDnsZoneFromNetworkCmdTest.java new file mode 100644 index 000000000000..34ef07efce3b --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/DisassociateDnsZoneFromNetworkCmdTest.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.user.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.junit.Test; + +import com.cloud.network.Network; +import com.cloud.user.Account; + +public class DisassociateDnsZoneFromNetworkCmdTest extends BaseDnsCmdTest { + + private static final long NETWORK_ID = 200L; + + private DisassociateDnsZoneFromNetworkCmd createCmd() throws Exception { + DisassociateDnsZoneFromNetworkCmd cmd = + new DisassociateDnsZoneFromNetworkCmd(); + setField(cmd, "dnsProviderManager", dnsProviderManager); + setField(cmd, "_entityMgr", entityManager); + setField(cmd, "networkId", NETWORK_ID); + return cmd; + } + + @Test + public void testGetNetworkId() throws Exception { + DisassociateDnsZoneFromNetworkCmd cmd = createCmd(); + assertEquals(Long.valueOf(NETWORK_ID), cmd.getNetworkId()); + } + + @Test + public void testGetEntityOwnerIdWithNetwork() throws Exception { + DisassociateDnsZoneFromNetworkCmd cmd = createCmd(); + Network mockNetwork = mock(Network.class); + when(mockNetwork.getAccountId()).thenReturn(ACCOUNT_ID); + when(entityManager.findById(Network.class, NETWORK_ID)) + .thenReturn(mockNetwork); + + assertEquals(ACCOUNT_ID, cmd.getEntityOwnerId()); + } + + @Test + public void testGetEntityOwnerIdNetworkNotFound() throws Exception { + DisassociateDnsZoneFromNetworkCmd cmd = createCmd(); + when(entityManager.findById(Network.class, NETWORK_ID)) + .thenReturn(null); + + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } + + @Test + public void testExecuteSuccess() throws Exception { + DisassociateDnsZoneFromNetworkCmd cmd = createCmd(); + when(dnsProviderManager.disassociateZoneFromNetwork(cmd)) + .thenReturn(true); + + cmd.execute(); + + SuccessResponse response = + (SuccessResponse) cmd.getResponseObject(); + assertNotNull(response); + verify(dnsProviderManager).disassociateZoneFromNetwork(cmd); + } + + @Test(expected = ServerApiException.class) + public void testExecuteReturnsFalse() throws Exception { + DisassociateDnsZoneFromNetworkCmd cmd = createCmd(); + when(dnsProviderManager.disassociateZoneFromNetwork(cmd)) + .thenReturn(false); + cmd.execute(); + } + + @Test(expected = ServerApiException.class) + public void testExecuteThrowsException() throws Exception { + DisassociateDnsZoneFromNetworkCmd cmd = createCmd(); + when(dnsProviderManager.disassociateZoneFromNetwork(cmd)) + .thenThrow(new RuntimeException("Error")); + cmd.execute(); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/dns/ListDnsProvidersCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/ListDnsProvidersCmdTest.java new file mode 100644 index 000000000000..62340742c878 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/ListDnsProvidersCmdTest.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.user.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; + +import org.apache.cloudstack.api.response.DnsProviderResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.junit.Test; + +public class ListDnsProvidersCmdTest extends BaseDnsCmdTest { + + private ListDnsProvidersCmd createCmd() throws Exception { + ListDnsProvidersCmd cmd = new ListDnsProvidersCmd(); + setField(cmd, "dnsProviderManager", dnsProviderManager); + return cmd; + } + + @Test + public void testExecute() throws Exception { + ListDnsProvidersCmd cmd = createCmd(); + when(dnsProviderManager.listProviderNames()).thenReturn(Arrays.asList("PowerDNS")); + + cmd.execute(); + + @SuppressWarnings("unchecked") + ListResponse response = + (ListResponse) cmd.getResponseObject(); + assertNotNull(response); + assertEquals("listdnsprovidersresponse", response.getResponseName()); + assertNotNull(response.getResponses()); + assertEquals(1, response.getResponses().size()); + } + + @Test + public void testExecuteMultipleProviders() throws Exception { + ListDnsProvidersCmd cmd = createCmd(); + when(dnsProviderManager.listProviderNames()) + .thenReturn(Arrays.asList("PowerDNS", "Cloudflare")); + + cmd.execute(); + + @SuppressWarnings("unchecked") + ListResponse response = + (ListResponse) cmd.getResponseObject(); + assertNotNull(response); + assertEquals(2, response.getResponses().size()); + } + + @Test + public void testExecuteEmptyList() throws Exception { + ListDnsProvidersCmd cmd = createCmd(); + when(dnsProviderManager.listProviderNames()) + .thenReturn(Collections.emptyList()); + + cmd.execute(); + + @SuppressWarnings("unchecked") + ListResponse response = + (ListResponse) cmd.getResponseObject(); + assertNotNull(response); + assertEquals(0, response.getResponses().size()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/dns/ListDnsRecordsCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/ListDnsRecordsCmdTest.java new file mode 100644 index 000000000000..e75713b55d57 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/ListDnsRecordsCmdTest.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.api.command.user.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.when; + +import org.apache.cloudstack.api.response.DnsRecordResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.junit.Test; + +public class ListDnsRecordsCmdTest extends BaseDnsCmdTest { + + private ListDnsRecordsCmd createCmd() throws Exception { + ListDnsRecordsCmd cmd = new ListDnsRecordsCmd(); + setField(cmd, "dnsProviderManager", dnsProviderManager); + setField(cmd, "dnsZoneId", ENTITY_ID); + return cmd; + } + + @Test + public void testGetDnsZoneId() throws Exception { + ListDnsRecordsCmd cmd = createCmd(); + assertEquals(Long.valueOf(ENTITY_ID), cmd.getDnsZoneId()); + } + + @Test + public void testExecute() throws Exception { + ListDnsRecordsCmd cmd = createCmd(); + + ListResponse mockListResponse = new ListResponse<>(); + when(dnsProviderManager.listDnsRecords(cmd)).thenReturn(mockListResponse); + + cmd.execute(); + + @SuppressWarnings("unchecked") + ListResponse response = (ListResponse) cmd.getResponseObject(); + assertNotNull(response); + assertEquals("listdnsrecordsresponse", response.getResponseName()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/dns/ListDnsServersCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/ListDnsServersCmdTest.java new file mode 100644 index 000000000000..710aa497167f --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/ListDnsServersCmdTest.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 org.apache.cloudstack.api.command.user.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.when; + +import org.apache.cloudstack.api.response.DnsServerResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.dns.DnsProviderType; +import org.junit.Test; + +public class ListDnsServersCmdTest extends BaseDnsCmdTest { + + private ListDnsServersCmd createCmd() throws Exception { + ListDnsServersCmd cmd = new ListDnsServersCmd(); + setField(cmd, "dnsProviderManager", dnsProviderManager); + setField(cmd, "id", ENTITY_ID); + setField(cmd, "providerType", "PowerDNS"); + return cmd; + } + + @Test + public void testAccessors() throws Exception { + ListDnsServersCmd cmd = createCmd(); + assertEquals(Long.valueOf(ENTITY_ID), cmd.getId()); + assertEquals(DnsProviderType.PowerDNS, cmd.getProviderType()); + } + + @Test + public void testGetProviderTypeNull() throws Exception { + ListDnsServersCmd cmd = createCmd(); + setField(cmd, "providerType", null); + assertEquals(DnsProviderType.PowerDNS, cmd.getProviderType()); + } + + @Test + public void testExecute() throws Exception { + ListDnsServersCmd cmd = createCmd(); + + ListResponse mockListResponse = new ListResponse<>(); + when(dnsProviderManager.listDnsServers(cmd)).thenReturn(mockListResponse); + + cmd.execute(); + + @SuppressWarnings("unchecked") + ListResponse response = (ListResponse) cmd.getResponseObject(); + assertNotNull(response); + assertEquals("listdnsserversresponse", response.getResponseName()); + assertEquals("dnsserver", response.getObjectName()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/dns/ListDnsZonesCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/ListDnsZonesCmdTest.java new file mode 100644 index 000000000000..7d47266ae6e6 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/ListDnsZonesCmdTest.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.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.when; + +import org.apache.cloudstack.api.response.DnsZoneResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.junit.Test; + +public class ListDnsZonesCmdTest extends BaseDnsCmdTest { + + private ListDnsZonesCmd createCmd() throws Exception { + ListDnsZonesCmd cmd = new ListDnsZonesCmd(); + setField(cmd, "dnsProviderManager", dnsProviderManager); + setField(cmd, "id", ENTITY_ID); + setField(cmd, "dnsServerId", 200L); + setField(cmd, "name", "example.com"); + return cmd; + } + + @Test + public void testAccessors() throws Exception { + ListDnsZonesCmd cmd = createCmd(); + assertEquals(Long.valueOf(ENTITY_ID), cmd.getId()); + assertEquals(Long.valueOf(200L), cmd.getDnsServerId()); + assertEquals("example.com", cmd.getName()); + } + + @Test + public void testExecute() throws Exception { + ListDnsZonesCmd cmd = createCmd(); + + ListResponse mockListResponse = new ListResponse<>(); + when(dnsProviderManager.listDnsZones(cmd)).thenReturn(mockListResponse); + + cmd.execute(); + + @SuppressWarnings("unchecked") + ListResponse response = (ListResponse) cmd.getResponseObject(); + assertNotNull(response); + assertEquals("listdnszonesresponse", response.getResponseName()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/dns/UpdateDnsServerCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/UpdateDnsServerCmdTest.java new file mode 100644 index 000000000000..f3c0fcd4fe07 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/UpdateDnsServerCmdTest.java @@ -0,0 +1,145 @@ +// 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.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; + +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DnsServerResponse; +import org.apache.cloudstack.dns.DnsServer; +import org.junit.Test; + +import com.cloud.user.Account; + +public class UpdateDnsServerCmdTest extends BaseDnsCmdTest { + + private UpdateDnsServerCmd createCmd() throws Exception { + UpdateDnsServerCmd cmd = new UpdateDnsServerCmd(); + setField(cmd, "dnsProviderManager", dnsProviderManager); + setField(cmd, "_entityMgr", entityManager); + setField(cmd, "id", ENTITY_ID); + setField(cmd, "name", "updated-dns"); + setField(cmd, "url", "http://updated.dns.com"); + setField(cmd, "dnsApiKey", "new-api-key"); + setField(cmd, "port", 9090); + setField(cmd, "isPublic", true); + setField(cmd, "publicDomainSuffix", "updated.example.com"); + setField(cmd, "nameServers", Arrays.asList("ns1.updated.com", "ns2.updated.com")); + setField(cmd, "state", "Enabled"); + return cmd; + } + + @Test + public void testAccessors() throws Exception { + UpdateDnsServerCmd cmd = createCmd(); + + assertEquals(Long.valueOf(ENTITY_ID), cmd.getId()); + assertEquals("updated-dns", cmd.getName()); + assertEquals("http://updated.dns.com", cmd.getUrl()); + assertEquals("new-api-key", cmd.getDnsApiKey()); + assertEquals(Integer.valueOf(9090), cmd.getPort()); + assertTrue(cmd.isPublic()); + assertEquals("updated.example.com", cmd.getPublicDomainSuffix()); + assertEquals("ns1.updated.com,ns2.updated.com", cmd.getNameServers()); + assertEquals(DnsServer.State.Enabled, cmd.getState()); + } + + @Test + public void testGetStateDisabled() throws Exception { + UpdateDnsServerCmd cmd = createCmd(); + setField(cmd, "state", "Disabled"); + assertEquals(DnsServer.State.Disabled, cmd.getState()); + } + + @Test + public void testGetStateNull() throws Exception { + UpdateDnsServerCmd cmd = createCmd(); + setField(cmd, "state", null); + assertNull(cmd.getState()); + } + + @Test + public void testGetStateBlank() throws Exception { + UpdateDnsServerCmd cmd = createCmd(); + setField(cmd, "state", ""); + assertNull(cmd.getState()); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetStateInvalid() throws Exception { + UpdateDnsServerCmd cmd = createCmd(); + setField(cmd, "state", "InvalidState"); + cmd.getState(); + } + + @Test + public void testGetEntityOwnerIdWithServer() throws Exception { + UpdateDnsServerCmd cmd = createCmd(); + DnsServer mockServer = mock(DnsServer.class); + when(mockServer.getAccountId()).thenReturn(ACCOUNT_ID); + when(entityManager.findById(DnsServer.class, ENTITY_ID)).thenReturn(mockServer); + + assertEquals(ACCOUNT_ID, cmd.getEntityOwnerId()); + } + + @Test + public void testGetEntityOwnerIdServerNotFound() throws Exception { + UpdateDnsServerCmd cmd = createCmd(); + when(entityManager.findById(DnsServer.class, ENTITY_ID)).thenReturn(null); + + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } + + @Test + public void testExecuteSuccess() throws Exception { + UpdateDnsServerCmd cmd = createCmd(); + + DnsServer mockServer = mock(DnsServer.class); + DnsServerResponse mockResponse = new DnsServerResponse(); + mockResponse.setName("updated-dns"); + + when(dnsProviderManager.updateDnsServer(cmd)).thenReturn(mockServer); + when(dnsProviderManager.createDnsServerResponse(mockServer)).thenReturn(mockResponse); + + cmd.execute(); + + DnsServerResponse response = (DnsServerResponse) cmd.getResponseObject(); + assertNotNull(response); + assertEquals("updatednsserverresponse", response.getResponseName()); + } + + @Test(expected = ServerApiException.class) + public void testExecuteReturnsNull() throws Exception { + UpdateDnsServerCmd cmd = createCmd(); + when(dnsProviderManager.updateDnsServer(cmd)).thenReturn(null); + cmd.execute(); + } + + @Test(expected = ServerApiException.class) + public void testExecuteThrowsException() throws Exception { + UpdateDnsServerCmd cmd = createCmd(); + when(dnsProviderManager.updateDnsServer(cmd)).thenThrow(new RuntimeException("Update failed")); + cmd.execute(); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/dns/UpdateDnsZoneCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/UpdateDnsZoneCmdTest.java new file mode 100644 index 000000000000..cac16903185a --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/UpdateDnsZoneCmdTest.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.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DnsZoneResponse; +import org.apache.cloudstack.dns.DnsZone; +import org.junit.Test; + +import com.cloud.user.Account; + +public class UpdateDnsZoneCmdTest extends BaseDnsCmdTest { + + private UpdateDnsZoneCmd createCmd() throws Exception { + UpdateDnsZoneCmd cmd = new UpdateDnsZoneCmd(); + setField(cmd, "dnsProviderManager", dnsProviderManager); + setField(cmd, "_entityMgr", entityManager); + setField(cmd, "id", ENTITY_ID); + setField(cmd, "description", "Updated description"); + return cmd; + } + + @Test + public void testAccessors() throws Exception { + UpdateDnsZoneCmd cmd = createCmd(); + assertEquals(Long.valueOf(ENTITY_ID), cmd.getId()); + assertEquals("Updated description", cmd.getDescription()); + } + + @Test + public void testGetEntityOwnerIdWhenZoneExists() throws Exception { + UpdateDnsZoneCmd cmd = createCmd(); + DnsZone mockZone = mock(DnsZone.class); + when(mockZone.getAccountId()).thenReturn(ACCOUNT_ID); + when(entityManager.findById(DnsZone.class, ENTITY_ID)).thenReturn(mockZone); + assertEquals(ACCOUNT_ID, cmd.getEntityOwnerId()); + } + + @Test + public void testGetEntityOwnerIdWhenZoneNotFound() throws Exception { + UpdateDnsZoneCmd cmd = createCmd(); + when(entityManager.findById(DnsZone.class, ENTITY_ID)).thenReturn(null); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } + + @Test + public void testExecuteSuccess() throws Exception { + UpdateDnsZoneCmd cmd = createCmd(); + + DnsZone mockZone = mock(DnsZone.class); + DnsZoneResponse mockResponse = new DnsZoneResponse(); + mockResponse.setName("example.com"); + + when(dnsProviderManager.updateDnsZone(cmd)).thenReturn(mockZone); + when(dnsProviderManager.createDnsZoneResponse(mockZone)).thenReturn(mockResponse); + + cmd.execute(); + + DnsZoneResponse response = (DnsZoneResponse) cmd.getResponseObject(); + assertNotNull(response); + assertEquals("updatednszoneresponse", response.getResponseName()); + } + + @Test(expected = ServerApiException.class) + public void testExecuteReturnsNull() throws Exception { + UpdateDnsZoneCmd cmd = createCmd(); + when(dnsProviderManager.updateDnsZone(cmd)).thenReturn(null); + cmd.execute(); + } + + @Test(expected = ServerApiException.class) + public void testExecuteThrowsException() throws Exception { + UpdateDnsZoneCmd cmd = createCmd(); + when(dnsProviderManager.updateDnsZone(cmd)).thenThrow(new RuntimeException("Update failed")); + cmd.execute(); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmdTest.java index c905974b2be9..504d3914b70e 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmdTest.java @@ -21,10 +21,15 @@ import java.util.Collections; import java.util.List; +import com.cloud.network.IpAddress; +import com.cloud.network.NetworkService; +import com.cloud.utils.db.EntityManager; import org.apache.commons.collections.CollectionUtils; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; @@ -33,6 +38,12 @@ @RunWith(MockitoJUnitRunner.class) public class CreateFirewallRuleCmdTest { + @Mock + private EntityManager entityManager; + + @Mock + private NetworkService networkService; + private void validateAllIp4Cidr(final CreateFirewallRuleCmd cmd) { Assert.assertTrue(CollectionUtils.isNotEmpty(cmd.getSourceCidrList())); Assert.assertEquals(1, cmd.getSourceCidrList().size()); @@ -88,4 +99,22 @@ public void testGetSourceCidrList_EmptyFirstElementButMore() { Assert.assertEquals(2, cmd.getSourceCidrList().size()); Assert.assertEquals(cidr, cmd.getSourceCidrList().get(1)); } + + @Test + public void testGetNetworkIdVpcWithoutAssociatedNetworkUsesVpcFallbackAndSyncObjId() { + final CreateFirewallRuleCmd cmd = new CreateFirewallRuleCmd(); + final IpAddress ip = Mockito.mock(IpAddress.class); + + cmd._entityMgr = entityManager; + cmd._networkService = networkService; + ReflectionTestUtils.setField(cmd, "ipAddressId", 42L); + + Mockito.when(networkService.getIp(42L)).thenReturn(ip); + Mockito.when(ip.getAssociatedWithNetworkId()).thenReturn(null); + Mockito.when(ip.getVpcId()).thenReturn(100L); + Mockito.when(ip.getNetworkId()).thenReturn(2L); + + Assert.assertEquals(Long.valueOf(2L), cmd.getNetworkId()); + Assert.assertEquals(Long.valueOf(2L), cmd.getSyncObjId()); + } } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/DeleteRoutingFirewallRuleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/DeleteRoutingFirewallRuleCmdTest.java index 2b55d4c6a58d..dbe7669431d8 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/DeleteRoutingFirewallRuleCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/DeleteRoutingFirewallRuleCmdTest.java @@ -23,6 +23,7 @@ import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.network.RoutedIpv4Manager; import org.junit.Assert; import org.junit.Test; @@ -31,6 +32,8 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; +import java.util.UUID; + import static org.junit.Assert.assertEquals; @RunWith(MockitoJUnitRunner.class) @@ -46,6 +49,7 @@ public void testProperties() { ReflectionTestUtils.setField(cmd, "_firewallService", _firewallService); long id = 1L; + String uuid = UUID.randomUUID().toString(); long accountId = 2L; long networkId = 3L; @@ -55,12 +59,14 @@ public void testProperties() { Mockito.when(_firewallService.getFirewallRule(id)).thenReturn(firewallRule); ReflectionTestUtils.setField(cmd, "id", id); + CallContext.current().putApiResourceUuid("id", uuid); + assertEquals(id, (long) cmd.getId()); assertEquals(accountId, cmd.getEntityOwnerId()); assertEquals(networkId, (long) cmd.getApiResourceId()); assertEquals(ApiCommandResourceType.Network, cmd.getApiResourceType()); assertEquals(EventTypes.EVENT_ROUTING_IPV4_FIREWALL_RULE_DELETE, cmd.getEventType()); - assertEquals(String.format("Deleting ipv4 routing firewall rule ID=%s", id), cmd.getEventDescription()); + assertEquals(String.format("Deleting IPv4 routing firewall rule with ID: %s", uuid), cmd.getEventDescription()); } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/snapshot/ListSnapshotPoliciesCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/snapshot/ListSnapshotPoliciesCmdTest.java new file mode 100644 index 000000000000..36cfcf5cb9a5 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/snapshot/ListSnapshotPoliciesCmdTest.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.user.snapshot; + +import com.cloud.storage.snapshot.SnapshotApiService; +import com.cloud.storage.snapshot.SnapshotPolicy; +import com.cloud.utils.Pair; +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.SnapshotPolicyResponse; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.List; + +public class ListSnapshotPoliciesCmdTest { + private ListSnapshotPoliciesCmd cmd; + private SnapshotApiService snapshotService; + private ResponseGenerator responseGenerator; + + @Before + public void setUp() { + cmd = new ListSnapshotPoliciesCmd(); + snapshotService = Mockito.mock(SnapshotApiService.class); + responseGenerator = Mockito.mock(ResponseGenerator.class); + + cmd._snapshotService = snapshotService; + cmd._responseGenerator = responseGenerator; + } + + @Test + public void testExecuteWithPolicies() { + SnapshotPolicy policy = Mockito.mock(SnapshotPolicy.class); + SnapshotPolicyResponse policyResponse = Mockito.mock(SnapshotPolicyResponse.class); + List policies = new ArrayList<>(); + policies.add(policy); + + Mockito.when(snapshotService.listSnapshotPolicies(cmd)) + .thenReturn(new Pair<>(policies, 1)); + Mockito.when(responseGenerator.createSnapshotPolicyResponse(policy)) + .thenReturn(policyResponse); + + cmd.execute(); + + ListResponse response = (ListResponse) cmd.getResponseObject(); + Assert.assertNotNull(response); + Assert.assertEquals(1, response.getResponses().size()); + Assert.assertEquals(policyResponse, response.getResponses().get(0)); + } + + @Test + public void testExecuteWithNoPolicies() { + Mockito.when(snapshotService.listSnapshotPolicies(cmd)) + .thenReturn(new Pair<>(new ArrayList<>(), 0)); + + cmd.execute(); + + ListResponse response = (ListResponse) cmd.getResponseObject(); + Assert.assertNotNull(response); + Assert.assertTrue(response.getResponses().isEmpty()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/userdata/RegisterUserDataCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/userdata/RegisterUserDataCmdTest.java index e9605526f86f..8fac32d8f92f 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/user/userdata/RegisterUserDataCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/userdata/RegisterUserDataCmdTest.java @@ -97,7 +97,7 @@ public void validateArgsCmd() { ReflectionTestUtils.setField(cmd, "name", "testUserdataName"); ReflectionTestUtils.setField(cmd, "userData", "testUserdata"); - when(_accountService.finalyzeAccountId(ACCOUNT_NAME, DOMAIN_ID, PROJECT_ID, true)).thenReturn(200L); + when(_accountService.finalizeAccountId(ACCOUNT_NAME, DOMAIN_ID, PROJECT_ID, true)).thenReturn(200L); Assert.assertEquals("testUserdataName", cmd.getName()); Assert.assertEquals("testUserdata", cmd.getUserData()); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vm/CreateVMScheduleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/CreateVMScheduleCmdTest.java index 99bc9d2b3fb7..ae848b2712b1 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/user/vm/CreateVMScheduleCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/CreateVMScheduleCmdTest.java @@ -21,8 +21,9 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.utils.db.EntityManager; import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.api.response.ResourceScheduleResponse; import org.apache.cloudstack.api.response.VMScheduleResponse; -import org.apache.cloudstack.vm.schedule.VMScheduleManager; +import org.apache.cloudstack.schedule.ResourceScheduleManager; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -34,9 +35,11 @@ public class CreateVMScheduleCmdTest { @Mock - public VMScheduleManager vmScheduleManager; + public ResourceScheduleManager resourceScheduleManager; + @Mock public EntityManager entityManager; + @InjectMocks private CreateVMScheduleCmd createVMScheduleCmd = new CreateVMScheduleCmd(); @@ -59,10 +62,19 @@ public void tearDown() throws Exception { */ @Test public void testSuccessfulExecution() { - VMScheduleResponse vmScheduleResponse = Mockito.mock(VMScheduleResponse.class); - Mockito.when(vmScheduleManager.createSchedule(createVMScheduleCmd)).thenReturn(vmScheduleResponse); + ResourceScheduleResponse scheduleResponse = new ResourceScheduleResponse(); + scheduleResponse.setId("schedule-uuid"); + scheduleResponse.setResourceId("vm-uuid"); + Mockito.when(resourceScheduleManager.createSchedule( + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), + Mockito.anyBoolean(), Mockito.any() + )).thenReturn(scheduleResponse); createVMScheduleCmd.execute(); - Assert.assertEquals(vmScheduleResponse, createVMScheduleCmd.getResponseObject()); + VMScheduleResponse response = (VMScheduleResponse) createVMScheduleCmd.getResponseObject(); + Assert.assertNotNull(response); + Assert.assertEquals("schedule-uuid", org.springframework.test.util.ReflectionTestUtils.getField(response, "id")); + Assert.assertEquals("vm-uuid", org.springframework.test.util.ReflectionTestUtils.getField(response, "vmId")); } /** @@ -72,7 +84,11 @@ public void testSuccessfulExecution() { */ @Test(expected = InvalidParameterValueException.class) public void testInvalidParameterValueException() { - Mockito.when(vmScheduleManager.createSchedule(createVMScheduleCmd)).thenThrow(InvalidParameterValueException.class); + Mockito.when(resourceScheduleManager.createSchedule( + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), + Mockito.anyBoolean(), Mockito.any() + )).thenThrow(new InvalidParameterValueException("Invalid schedule")); createVMScheduleCmd.execute(); } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vm/DeleteVMScheduleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/DeleteVMScheduleCmdTest.java index 1f764a84365f..cec7503bd52b 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/user/vm/DeleteVMScheduleCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/DeleteVMScheduleCmdTest.java @@ -23,8 +23,7 @@ import com.cloud.vm.VirtualMachine; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.SuccessResponse; -import org.apache.cloudstack.vm.schedule.VMSchedule; -import org.apache.cloudstack.vm.schedule.VMScheduleManager; +import org.apache.cloudstack.schedule.ResourceScheduleManager; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -36,7 +35,8 @@ public class DeleteVMScheduleCmdTest { @Mock - public VMScheduleManager vmScheduleManager; + public ResourceScheduleManager resourceScheduleManager; + @Mock public EntityManager entityManager; @@ -64,9 +64,11 @@ public void tearDown() throws Exception { public void testSuccessfulExecution() { final SuccessResponse response = new SuccessResponse(); response.setResponseName(deleteVMScheduleCmd.getCommandName()); - response.setObjectName(VMSchedule.class.getSimpleName().toLowerCase()); + response.setObjectName("vmschedule"); - Mockito.when(vmScheduleManager.removeSchedule(deleteVMScheduleCmd)).thenReturn(1L); + Mockito.when(resourceScheduleManager.removeSchedule( + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any() + )).thenReturn(1L); deleteVMScheduleCmd.execute(); SuccessResponse actualResponse = (SuccessResponse) deleteVMScheduleCmd.getResponseObject(); Assert.assertEquals(response.getResponseName(), actualResponse.getResponseName()); @@ -80,7 +82,9 @@ public void testSuccessfulExecution() { */ @Test(expected = ServerApiException.class) public void testServerApiException() { - Mockito.when(vmScheduleManager.removeSchedule(deleteVMScheduleCmd)).thenReturn(0L); + Mockito.when(resourceScheduleManager.removeSchedule( + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any() + )).thenReturn(0L); deleteVMScheduleCmd.execute(); } @@ -91,7 +95,9 @@ public void testServerApiException() { */ @Test(expected = InvalidParameterValueException.class) public void testInvalidParameterValueException() { - Mockito.when(vmScheduleManager.removeSchedule(deleteVMScheduleCmd)).thenThrow(InvalidParameterValueException.class); + Mockito.when(resourceScheduleManager.removeSchedule( + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any() + )).thenThrow(new InvalidParameterValueException("Invalid schedule")); deleteVMScheduleCmd.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 index f7e3e38d9c3f..09d396e40237 100644 --- 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 @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.user.vm; 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; @@ -41,6 +42,7 @@ import org.springframework.test.util.ReflectionTestUtils; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.NetworkService; import com.cloud.utils.db.EntityManager; import com.cloud.vm.VmDetailConstants; @@ -480,4 +482,146 @@ public void testGetDataDiskTemplateToDiskOfferingMapInvalidTemplateId() { }); assertTrue(thrownException.getMessage().contains("Unable to translate and find entity with datadisktemplateid")); } + + @Test + public void testSetServiceOfferingId() { + cmd.setServiceOfferingId(101L); + assertEquals(Long.valueOf(101L), cmd.getServiceOfferingId()); + } + + @Test + public void testSetTemplateId() { + cmd.setTemplateId(102L); + assertEquals(Long.valueOf(102L), cmd.getTemplateId()); + } + + @Test + public void testSetVolumeId() { + cmd.setVolumeId(103L); + assertEquals(Long.valueOf(103L), cmd.getVolumeId()); + } + + @Test + public void testSetSnapshotId() { + cmd.setSnapshotId(104L); + assertEquals(Long.valueOf(104L), cmd.getSnapshotId()); + } + + @Test + public void testSetZoneId() { + cmd.setZoneId(105L); + assertEquals(Long.valueOf(105L), cmd.getZoneId()); + } + + @Test + public void testSetName() { + cmd.setName("vm-name"); + assertEquals("vm-name", cmd.getName()); + } + + @Test + public void testSetDisplayName() { + cmd.setDisplayName("vm-display-name"); + assertEquals("vm-display-name", cmd.getDisplayName()); + } + + @Test + public void testSetAccountName() { + cmd.setAccountName("account-name"); + assertEquals("account-name", cmd.getAccountName()); + } + + @Test + public void testSetDomainId() { + cmd.setDomainId(106L); + assertEquals(Long.valueOf(106L), cmd.getDomainId()); + } + + @Test + public void testSetNetworkIds() { + List networkIds = Arrays.asList(11L, 12L); + cmd.setNetworkIds(networkIds); + assertEquals(networkIds, cmd.getNetworkIds()); + } + + @Test + public void testSetBootType() { + cmd.setBootType("UEFI"); + assertEquals(BootType.UEFI, cmd.getBootType()); + } + + @Test + public void testSetBootMode() { + cmd.setBootType("UEFI"); + cmd.setBootMode("SECURE"); + assertEquals(BootMode.SECURE, cmd.getBootMode()); + } + + @Test + public void testSetHypervisor() { + cmd.setHypervisor("KVM"); + assertEquals(HypervisorType.KVM, cmd.getHypervisor()); + } + + @Test + public void testSetUserData() { + cmd.setUserData("dXNlci1kYXRh"); + assertEquals("dXNlci1kYXRh", cmd.getUserData()); + } + + @Test + public void testSetKeyboard() { + cmd.setKeyboard("us"); + assertEquals("us", cmd.getKeyboard()); + } + + @Test + public void testSetProjectId() { + cmd.setProjectId(107L); + assertEquals(Long.valueOf(107L), ReflectionTestUtils.getField(cmd, "projectId")); + } + + @Test + public void testSetDisplayVm() { + cmd.setDisplayVm(Boolean.FALSE); + assertEquals(Boolean.FALSE, cmd.isDisplayVm()); + } + + @Test + public void testSetUserDataId() { + cmd.setUserDataId(108L); + assertEquals(Long.valueOf(108L), cmd.getUserdataId()); + } + + @Test + public void testSetAffinityGroupIds() { + List affinityGroupIds = Arrays.asList(21L, 22L); + cmd.setAffinityGroupIds(affinityGroupIds); + assertEquals(affinityGroupIds, cmd.getAffinityGroupIdList()); + } + + @Test + public void testSetDetails() { + Map details = new HashMap<>(); + details.put("key", "value"); + cmd.setDetails(details); + assertEquals(details, ReflectionTestUtils.getField(cmd, "details")); + } + + @Test + public void testSetExtraConfig() { + cmd.setExtraConfig("cpu-mode=host-passthrough"); + assertEquals("cpu-mode=host-passthrough", cmd.getExtraConfig()); + } + + @Test + public void testSetDynamicScalingEnabled() { + cmd.setDynamicScalingEnabled(Boolean.FALSE); + assertFalse(cmd.isDynamicScalingEnabled()); + } + + @Test + public void testIsBlankInstance() { + assertFalse(cmd.isBlankInstance()); + } } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vm/ListVMScheduleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/ListVMScheduleCmdTest.java index f5434de3581c..5449be17a576 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/user/vm/ListVMScheduleCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/ListVMScheduleCmdTest.java @@ -20,8 +20,9 @@ import com.cloud.exception.InvalidParameterValueException; import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ResourceScheduleResponse; import org.apache.cloudstack.api.response.VMScheduleResponse; -import org.apache.cloudstack.vm.schedule.VMScheduleManager; +import org.apache.cloudstack.schedule.ResourceScheduleManager; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -36,7 +37,8 @@ public class ListVMScheduleCmdTest { @Mock - public VMScheduleManager vmScheduleManager; + public ResourceScheduleManager resourceScheduleManager; + @InjectMocks private ListVMScheduleCmd listVMScheduleCmd = new ListVMScheduleCmd(); private AutoCloseable closeable; @@ -58,12 +60,14 @@ public void tearDown() throws Exception { */ @Test public void testEmptyResponse() { - ListResponse response = new ListResponse(); - response.setResponses(new ArrayList()); - Mockito.when(vmScheduleManager.listSchedule(listVMScheduleCmd)).thenReturn(response); + ListResponse response = new ListResponse(); + response.setResponses(new ArrayList()); + Mockito.when(resourceScheduleManager.listSchedule( + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any() + )).thenReturn(response); listVMScheduleCmd.execute(); ListResponse actualResponseObject = (ListResponse) listVMScheduleCmd.getResponseObject(); - Assert.assertEquals(response, actualResponseObject); Assert.assertEquals(0L, actualResponseObject.getResponses().size()); } @@ -74,15 +78,22 @@ public void testEmptyResponse() { */ @Test public void testNonEmptyResponse() { - ListResponse listResponse = new ListResponse(); - VMScheduleResponse response = Mockito.mock(VMScheduleResponse.class); + ListResponse listResponse = new ListResponse(); + ResourceScheduleResponse response = new ResourceScheduleResponse(); + response.setId("schedule-uuid"); + response.setResourceId("vm-uuid"); listResponse.setResponses(Collections.singletonList(response)); - Mockito.when(vmScheduleManager.listSchedule(listVMScheduleCmd)).thenReturn(listResponse); + Mockito.when(resourceScheduleManager.listSchedule( + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any() + )).thenReturn(listResponse); listVMScheduleCmd.execute(); ListResponse actualResponseObject = (ListResponse) listVMScheduleCmd.getResponseObject(); - Assert.assertEquals(listResponse, actualResponseObject); Assert.assertEquals(1L, actualResponseObject.getResponses().size()); - Assert.assertEquals(response, actualResponseObject.getResponses().get(0)); + Assert.assertEquals("schedule-uuid", + org.springframework.test.util.ReflectionTestUtils.getField(actualResponseObject.getResponses().get(0), "id")); + Assert.assertEquals("vm-uuid", + org.springframework.test.util.ReflectionTestUtils.getField(actualResponseObject.getResponses().get(0), "vmId")); } /** @@ -92,7 +103,10 @@ public void testNonEmptyResponse() { */ @Test(expected = InvalidParameterValueException.class) public void testInvalidParameterValueException() { - Mockito.when(vmScheduleManager.listSchedule(listVMScheduleCmd)).thenThrow(InvalidParameterValueException.class); + Mockito.when(resourceScheduleManager.listSchedule( + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any() + )).thenThrow(InvalidParameterValueException.class); listVMScheduleCmd.execute(); ListResponse actualResponseObject = (ListResponse) listVMScheduleCmd.getResponseObject(); } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vm/UpdateVMScheduleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/UpdateVMScheduleCmdTest.java index 2c6c485f25bf..a24252f4a1d0 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/user/vm/UpdateVMScheduleCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/UpdateVMScheduleCmdTest.java @@ -21,9 +21,11 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.utils.db.EntityManager; import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.response.ResourceScheduleResponse; import org.apache.cloudstack.api.response.VMScheduleResponse; -import org.apache.cloudstack.vm.schedule.VMSchedule; -import org.apache.cloudstack.vm.schedule.VMScheduleManager; +import org.apache.cloudstack.schedule.ResourceSchedule; +import org.apache.cloudstack.schedule.ResourceScheduleManager; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -35,9 +37,11 @@ public class UpdateVMScheduleCmdTest { @Mock - public VMScheduleManager vmScheduleManager; + public ResourceScheduleManager resourceScheduleManager; + @Mock public EntityManager entityManager; + @InjectMocks private UpdateVMScheduleCmd updateVMScheduleCmd = new UpdateVMScheduleCmd(); @@ -60,10 +64,18 @@ public void tearDown() throws Exception { */ @Test public void testSuccessfulExecution() { - VMScheduleResponse vmScheduleResponse = Mockito.mock(VMScheduleResponse.class); - Mockito.when(vmScheduleManager.updateSchedule(updateVMScheduleCmd)).thenReturn(vmScheduleResponse); + ResourceScheduleResponse scheduleResponse = new ResourceScheduleResponse(); + scheduleResponse.setId("schedule-uuid"); + scheduleResponse.setResourceId("vm-uuid"); + Mockito.when(resourceScheduleManager.updateSchedule( + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any() + )).thenReturn(scheduleResponse); updateVMScheduleCmd.execute(); - Assert.assertEquals(vmScheduleResponse, updateVMScheduleCmd.getResponseObject()); + VMScheduleResponse response = (VMScheduleResponse) updateVMScheduleCmd.getResponseObject(); + Assert.assertNotNull(response); + Assert.assertEquals("schedule-uuid", org.springframework.test.util.ReflectionTestUtils.getField(response, "id")); + Assert.assertEquals("vm-uuid", org.springframework.test.util.ReflectionTestUtils.getField(response, "vmId")); } /** @@ -73,7 +85,10 @@ public void testSuccessfulExecution() { */ @Test(expected = InvalidParameterValueException.class) public void testInvalidParameterValueException() { - Mockito.when(vmScheduleManager.updateSchedule(updateVMScheduleCmd)).thenThrow(InvalidParameterValueException.class); + Mockito.when(resourceScheduleManager.updateSchedule( + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any() + )).thenThrow(new InvalidParameterValueException("Invalid schedule")); updateVMScheduleCmd.execute(); } @@ -84,11 +99,12 @@ public void testInvalidParameterValueException() { */ @Test public void testSuccessfulGetEntityOwnerId() { - VMSchedule vmSchedule = Mockito.mock(VMSchedule.class); + ResourceSchedule schedule = Mockito.mock(ResourceSchedule.class); VirtualMachine vm = Mockito.mock(VirtualMachine.class); - Mockito.when(entityManager.findById(VMSchedule.class, updateVMScheduleCmd.getId())).thenReturn(vmSchedule); - Mockito.when(entityManager.findById(VirtualMachine.class, vmSchedule.getVmId())).thenReturn(vm); + Mockito.when(schedule.getResourceType()).thenReturn(ApiCommandResourceType.VirtualMachine); + Mockito.when(entityManager.findById(ResourceSchedule.class, updateVMScheduleCmd.getId())).thenReturn(schedule); + Mockito.when(entityManager.findById(VirtualMachine.class, schedule.getResourceId())).thenReturn(vm); long ownerId = updateVMScheduleCmd.getEntityOwnerId(); Assert.assertEquals(vm.getAccountId(), ownerId); @@ -101,8 +117,7 @@ public void testSuccessfulGetEntityOwnerId() { */ @Test(expected = InvalidParameterValueException.class) public void testFailureGetEntityOwnerId() { - VMSchedule vmSchedule = Mockito.mock(VMSchedule.class); - Mockito.when(entityManager.findById(VMSchedule.class, updateVMScheduleCmd.getId())).thenReturn(null); - long ownerId = updateVMScheduleCmd.getEntityOwnerId(); + Mockito.when(entityManager.findById(ResourceSchedule.class, updateVMScheduleCmd.getId())).thenReturn(null); + updateVMScheduleCmd.getEntityOwnerId(); } } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmdTest.java index acb2dc685976..9d56cffd6718 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmdTest.java @@ -93,7 +93,7 @@ public void testExecute() throws ResourceUnavailableException, InsufficientCapac responseGenerator = Mockito.mock(ResponseGenerator.class); cmd._responseGenerator = responseGenerator; Mockito.verify(_vpcService, Mockito.times(0)).updateVpc(Mockito.anyLong(), Mockito.anyString(), Mockito.anyString(), - Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt(), Mockito.anyString()); + Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt(), Mockito.anyString(), Mockito.anyBoolean()); } } diff --git a/server/src/test/java/org/apache/cloudstack/storage/heuristics/presetvariables/AccountTest.java b/api/src/test/java/org/apache/cloudstack/api/response/AttachedIsoResponseTest.java similarity index 50% rename from server/src/test/java/org/apache/cloudstack/storage/heuristics/presetvariables/AccountTest.java rename to api/src/test/java/org/apache/cloudstack/api/response/AttachedIsoResponseTest.java index f7610438b78c..09d4eb598ab0 100644 --- a/server/src/test/java/org/apache/cloudstack/storage/heuristics/presetvariables/AccountTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/response/AttachedIsoResponseTest.java @@ -14,33 +14,33 @@ // 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; -package org.apache.cloudstack.storage.heuristics.presetvariables; - -import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) -public class AccountTest { +public final class AttachedIsoResponseTest { @Test - public void toStringTestReturnsValidJson() { - Account variable = new Account(); - variable.setName("test name"); - variable.setId("test id"); - - Domain domainVariable = new Domain(); - domainVariable.setId("domain id"); - domainVariable.setName("domain name"); - variable.setDomain(domainVariable); - - String expected = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(variable, "name", "id", "domain"); - String result = variable.toString(); - - Assert.assertEquals(expected, result); + public void testFullConstructorPopulatesAllFields() { + AttachedIsoResponse response = new AttachedIsoResponse("uuid-1", "alpine-iso", "Alpine boot", 3, true); + Assert.assertEquals("uuid-1", response.getId()); + Assert.assertEquals("alpine-iso", response.getName()); + Assert.assertEquals("Alpine boot", response.getDisplayText()); + Assert.assertEquals(Integer.valueOf(3), response.getDeviceSeq()); + Assert.assertTrue(response.getBootable()); } + @Test + public void testNoArgConstructorLeavesFieldsNull() { + AttachedIsoResponse response = new AttachedIsoResponse(); + Assert.assertNull(response.getId()); + Assert.assertNull(response.getName()); + Assert.assertNull(response.getDisplayText()); + Assert.assertNull(response.getDeviceSeq()); + Assert.assertNull(response.getBootable()); + } } diff --git a/api/src/test/java/org/apache/cloudstack/api/response/LoginCmdResponseTest.java b/api/src/test/java/org/apache/cloudstack/api/response/LoginCmdResponseTest.java new file mode 100644 index 000000000000..7811138fffe1 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/response/LoginCmdResponseTest.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.response; + + +import org.junit.Assert; +import org.junit.Test; + +public class LoginCmdResponseTest { + + @Test + public void testAllGettersAndSetters() { + LoginCmdResponse response = new LoginCmdResponse(); + + response.setUsername("user1"); + response.setUserId("100"); + response.setDomainId("200"); + response.setTimeout(3600); + response.setAccount("account1"); + response.setFirstName("John"); + response.setLastName("Doe"); + response.setType("admin"); + response.setTimeZone("UTC"); + response.setTimeZoneOffset("+00:00"); + response.setRegistered("true"); + response.setSessionKey("session-key"); + response.set2FAenabled("true"); + response.set2FAverfied("false"); + response.setProviderFor2FA("totp"); + response.setIssuerFor2FA("cloudstack"); + response.setManagementServerId("ms-1"); + + Assert.assertEquals("user1", response.getUsername()); + Assert.assertEquals("100", response.getUserId()); + Assert.assertEquals("200", response.getDomainId()); + Assert.assertEquals(Integer.valueOf(3600), response.getTimeout()); + Assert.assertEquals("account1", response.getAccount()); + Assert.assertEquals("John", response.getFirstName()); + Assert.assertEquals("Doe", response.getLastName()); + Assert.assertEquals("admin", response.getType()); + Assert.assertEquals("UTC", response.getTimeZone()); + Assert.assertEquals("+00:00", response.getTimeZoneOffset()); + Assert.assertEquals("true", response.getRegistered()); + Assert.assertEquals("session-key", response.getSessionKey()); + Assert.assertEquals("true", response.is2FAenabled()); + Assert.assertEquals("false", response.is2FAverfied()); + Assert.assertEquals("totp", response.getProviderFor2FA()); + Assert.assertEquals("cloudstack", response.getIssuerFor2FA()); + Assert.assertEquals("ms-1", response.getManagementServerId()); + } + + @Test + public void testPasswordChangeRequired_True() { + LoginCmdResponse response = new LoginCmdResponse(); + response.setPasswordChangeRequired(true); + Assert.assertTrue(response.getPasswordChangeRequired()); + } + + @Test + public void testPasswordChangeRequired_False() { + LoginCmdResponse response = new LoginCmdResponse(); + response.setPasswordChangeRequired(false); + Assert.assertFalse(response.getPasswordChangeRequired()); + } + + @Test + public void testPasswordChangeRequired_Null() { + LoginCmdResponse response = new LoginCmdResponse(); + response.setPasswordChangeRequired(null); + Assert.assertNull("Boolean.parseBoolean(null) should return null", response.getPasswordChangeRequired()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/config/ApiServiceConfigurationTest.java b/api/src/test/java/org/apache/cloudstack/config/ApiServiceConfigurationTest.java new file mode 100644 index 000000000000..4e96af3ead41 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/config/ApiServiceConfigurationTest.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.config; + +import com.cloud.exception.InvalidParameterValueException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class ApiServiceConfigurationTest { + + private static final String LOCALHOST = "http://localhost"; + + private static final String ENDPOINT_URL = "https://acs.clouds.com/client/api"; + + private static final String WHITE_SPACE = " "; + + private static final String BLANK_STRING = ""; + + private static final String NULL_STRING = null; + + private static final String LOCALHOST_IP = "127.0.0.1"; + + @Test(expected = InvalidParameterValueException.class) + public void validateEndpointUrlTestIfEndpointUrlContainLocalhostShouldThrowInvalidParameterValueException() { + try (MockedStatic apiServiceConfigurationMockedStatic = Mockito.mockStatic(ApiServiceConfiguration.class)) { + apiServiceConfigurationMockedStatic.when(ApiServiceConfiguration::getApiServletPathValue).thenReturn(LOCALHOST); + apiServiceConfigurationMockedStatic.when(ApiServiceConfiguration::validateEndpointUrl).thenCallRealMethod(); + ApiServiceConfiguration.validateEndpointUrl(); + } + } + + @Test + public void validateEndpointUrlTestIfEndpointUrlContainLocalhostShouldNotThrowInvalidParameterValueException() { + try (MockedStatic apiServiceConfigurationMockedStatic = Mockito.mockStatic(ApiServiceConfiguration.class)) { + apiServiceConfigurationMockedStatic.when(ApiServiceConfiguration::getApiServletPathValue).thenReturn(ENDPOINT_URL); + apiServiceConfigurationMockedStatic.when(ApiServiceConfiguration::validateEndpointUrl).thenCallRealMethod(); + ApiServiceConfiguration.validateEndpointUrl(); + } + } + + @Test(expected = InvalidParameterValueException.class) + public void validateEndpointUrlTestIfEndpointUrlIsNullShouldThrowInvalidParameterValueException() { + try (MockedStatic apiServiceConfigurationMockedStatic = Mockito.mockStatic(ApiServiceConfiguration.class)) { + apiServiceConfigurationMockedStatic.when(ApiServiceConfiguration::getApiServletPathValue).thenReturn(NULL_STRING); + apiServiceConfigurationMockedStatic.when(ApiServiceConfiguration::validateEndpointUrl).thenCallRealMethod(); + ApiServiceConfiguration.validateEndpointUrl(); + } + } + + @Test(expected = InvalidParameterValueException.class) + public void validateEndpointUrlTestIfEndpointUrlIsBlankShouldThrowInvalidParameterValueException() { + try (MockedStatic apiServiceConfigurationMockedStatic = Mockito.mockStatic(ApiServiceConfiguration.class)) { + apiServiceConfigurationMockedStatic.when(ApiServiceConfiguration::getApiServletPathValue).thenReturn(BLANK_STRING); + apiServiceConfigurationMockedStatic.when(ApiServiceConfiguration::validateEndpointUrl).thenCallRealMethod(); + ApiServiceConfiguration.validateEndpointUrl(); + } + } + + @Test(expected = InvalidParameterValueException.class) + public void validateEndpointUrlTestIfEndpointUrlIsWhiteSpaceShouldThrowInvalidParameterValueException() { + try (MockedStatic apiServiceConfigurationMockedStatic = Mockito.mockStatic(ApiServiceConfiguration.class)) { + apiServiceConfigurationMockedStatic.when(ApiServiceConfiguration::getApiServletPathValue).thenReturn(WHITE_SPACE); + apiServiceConfigurationMockedStatic.when(ApiServiceConfiguration::validateEndpointUrl).thenCallRealMethod(); + ApiServiceConfiguration.validateEndpointUrl(); + } + } + + @Test(expected = InvalidParameterValueException.class) + public void validateEndpointUrlTestIfEndpointUrlContainLocalhostIpShouldThrowInvalidParameterValueException() { + try (MockedStatic apiServiceConfigurationMockedStatic = Mockito.mockStatic(ApiServiceConfiguration.class)) { + apiServiceConfigurationMockedStatic.when(ApiServiceConfiguration::getApiServletPathValue).thenReturn(LOCALHOST_IP); + apiServiceConfigurationMockedStatic.when(ApiServiceConfiguration::validateEndpointUrl).thenCallRealMethod(); + ApiServiceConfiguration.validateEndpointUrl(); + } + } +} diff --git a/api/src/test/java/org/apache/cloudstack/dns/DnsRecordTest.java b/api/src/test/java/org/apache/cloudstack/dns/DnsRecordTest.java new file mode 100644 index 000000000000..bd993380e068 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/dns/DnsRecordTest.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 org.apache.cloudstack.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; + +import com.cloud.utils.exception.CloudRuntimeException; + +public class DnsRecordTest { + + @Test + public void testDefaultConstructor() { + DnsRecord record = new DnsRecord(); + assertNull(record.getName()); + assertNull(record.getType()); + assertNull(record.getContents()); + assertEquals(0, record.getTtl()); + } + + @Test + public void testParameterizedConstructor() { + List contents = Arrays.asList("192.168.1.1"); + DnsRecord record = new DnsRecord("www", DnsRecord.RecordType.A, contents, 3600); + + assertEquals("www", record.getName()); + assertEquals(DnsRecord.RecordType.A, record.getType()); + assertEquals(contents, record.getContents()); + assertEquals(3600, record.getTtl()); + } + + @Test + public void testSettersAndGetters() { + DnsRecord record = new DnsRecord(); + List contents = Arrays.asList("10.0.0.1", "10.0.0.2"); + + record.setName("mail"); + record.setType(DnsRecord.RecordType.AAAA); + record.setContents(contents); + record.setTtl(7200); + + assertEquals("mail", record.getName()); + assertEquals(DnsRecord.RecordType.AAAA, record.getType()); + assertEquals(contents, record.getContents()); + assertEquals(7200, record.getTtl()); + } + + // RecordType.fromString tests + + @Test + public void testFromStringValid() { + assertEquals(DnsRecord.RecordType.A, DnsRecord.RecordType.fromString("A")); + assertEquals(DnsRecord.RecordType.AAAA, DnsRecord.RecordType.fromString("AAAA")); + assertEquals(DnsRecord.RecordType.CNAME, DnsRecord.RecordType.fromString("CNAME")); + assertEquals(DnsRecord.RecordType.MX, DnsRecord.RecordType.fromString("MX")); + assertEquals(DnsRecord.RecordType.TXT, DnsRecord.RecordType.fromString("TXT")); + assertEquals(DnsRecord.RecordType.SRV, DnsRecord.RecordType.fromString("SRV")); + assertEquals(DnsRecord.RecordType.PTR, DnsRecord.RecordType.fromString("PTR")); + assertEquals(DnsRecord.RecordType.NS, DnsRecord.RecordType.fromString("NS")); + } + + @Test + public void testFromStringCaseInsensitive() { + assertEquals(DnsRecord.RecordType.A, DnsRecord.RecordType.fromString("a")); + assertEquals(DnsRecord.RecordType.CNAME, DnsRecord.RecordType.fromString("cname")); + assertEquals(DnsRecord.RecordType.MX, DnsRecord.RecordType.fromString("mx")); + } + + @Test + public void testFromStringNull() { + assertNull(DnsRecord.RecordType.fromString(null)); + } + + @Test(expected = CloudRuntimeException.class) + public void testFromStringInvalid() { + DnsRecord.RecordType.fromString("INVALID"); + } + + @Test + public void testRecordTypeValues() { + DnsRecord.RecordType[] values = DnsRecord.RecordType.values(); + assertNotNull(values); + assertEquals(8, values.length); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/extension/CustomActionResultResponseTest.java b/api/src/test/java/org/apache/cloudstack/extension/CustomActionResultResponseTest.java new file mode 100644 index 000000000000..9b7346f332cb --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/extension/CustomActionResultResponseTest.java @@ -0,0 +1,92 @@ +// 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.Map; + +import org.junit.Before; +import org.junit.Test; + +public class CustomActionResultResponseTest { + + private CustomActionResultResponse response; + + @Before + public void setUp() { + response = new CustomActionResultResponse(); + } + + @Test + public void getResultReturnsNullByDefault() { + assertNull(response.getResult()); + } + + @Test + public void getResultReturnsSetValue() { + Map result = Map.of("message", "OK", "details", "All good"); + response.setResult(result); + assertEquals(result, response.getResult()); + assertEquals("OK", response.getResult().get("message")); + } + + @Test + public void isSuccessReturnsFalseWhenSuccessIsNull() { + // success is null by default + assertFalse(response.isSuccess()); + } + + @Test + public void isSuccessReturnsFalseWhenSuccessIsFalse() { + response.setSuccess(false); + assertFalse(response.isSuccess()); + } + + @Test + public void isSuccessReturnsTrueWhenSuccessIsTrue() { + response.setSuccess(true); + assertTrue(response.isSuccess()); + } + + @Test + public void getSuccessReturnsNullByDefault() { + assertNull(response.getSuccess()); + } + + @Test + public void getSuccessReturnsTrueAfterSetSuccessTrue() { + response.setSuccess(true); + assertTrue(response.getSuccess()); + } + + @Test + public void getSuccessReturnsFalseAfterSetSuccessFalse() { + response.setSuccess(false); + assertFalse(response.getSuccess()); + } + + @Test + public void setAndGetResultWithNullResult() { + response.setResult(null); + assertNull(response.getResult()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/extension/ExtensionCustomActionTest.java b/api/src/test/java/org/apache/cloudstack/extension/ExtensionCustomActionTest.java index ae4314aa11a8..a6bd49a2d8be 100644 --- a/api/src/test/java/org/apache/cloudstack/extension/ExtensionCustomActionTest.java +++ b/api/src/test/java/org/apache/cloudstack/extension/ExtensionCustomActionTest.java @@ -40,6 +40,12 @@ public void testResourceType() { assertEquals(com.cloud.vm.VirtualMachine.class, vmType.getAssociatedClass()); } + @Test + public void testNetworkResourceType() { + ExtensionCustomAction.ResourceType networkType = ExtensionCustomAction.ResourceType.Network; + assertEquals(com.cloud.network.Network.class, networkType.getAssociatedClass()); + } + @Test public void testParameterTypeSupportsOptions() { assertTrue(ExtensionCustomAction.Parameter.Type.STRING.canSupportsOptions()); diff --git a/client/bindir/cloud-setup-management.in b/client/bindir/cloud-setup-management.in index 84c87ae2e442..b4fe76cc8d8a 100755 --- a/client/bindir/cloud-setup-management.in +++ b/client/bindir/cloud-setup-management.in @@ -36,6 +36,106 @@ from cloudutils.cloudException import CloudRuntimeException, CloudInternalExcept from cloudutils.globalEnv import globalEnv from cloudutils.serviceConfigServer import cloudManagementConfig from optparse import OptionParser +import urllib.request +import configparser +import hashlib + +SYSTEMVM_TEMPLATES_PATH = "/usr/share/cloudstack-management/templates/systemvm" +SYSTEMVM_TEMPLATES_METADATA_FILE = SYSTEMVM_TEMPLATES_PATH + "/metadata.ini" + +def verify_sha512_checksum(file_path, expected_checksum): + sha512 = hashlib.sha512() + try: + with open(file_path, "rb") as f: + for chunk in iter(lambda: f.read(8192), b""): + sha512.update(chunk) + return sha512.hexdigest().lower() == expected_checksum.lower() + except Exception as e: + print(f"Failed to verify checksum for {file_path}: {e}") + return False + +def download_file(url, dest_path, chunk_size=8 * 1024 * 1024): + """ + Downloads a file from the given URL to the specified destination path in chunks. + """ + try: + with urllib.request.urlopen(url) as response: + total_size = response.length if response.length else None + downloaded = 0 + try: + with open(dest_path, 'wb') as out_file: + while True: + chunk = response.read(chunk_size) + if not chunk: + break + out_file.write(chunk) + downloaded += len(chunk) + if total_size: + print(f"Downloaded {downloaded / (1024 * 1024):.2f}MB of {total_size / (1024 * 1024):.2f}MB", end='\r') + except PermissionError as pe: + print(f"Permission denied: {dest_path}") + raise + print(f"\nDownloaded file from {url} to {dest_path}") + except Exception as e: + print(f"Failed to download file: {e}") + raise + +def download_template_if_needed(template, url, filename, checksum): + dest_path = os.path.join(SYSTEMVM_TEMPLATES_PATH, filename) + if os.path.exists(dest_path): + if checksum and verify_sha512_checksum(dest_path, checksum): + print(f"{template} System VM template already exists at {dest_path} with valid checksum, skipping download.") + return + else: + print(f"{template} System VM template at {dest_path} has invalid or missing checksum, re-downloading...") + else: + print(f"Downloading {template} System VM template from {url} to {dest_path}...") + try: + download_file(url, dest_path) + #After download, verify checksum if provided + if checksum: + if verify_sha512_checksum(dest_path, checksum): + print(f"{template} System VM template downloaded and verified successfully.") + else: + print(f"ERROR: Checksum verification failed for {template} System VM template after download.") + except Exception as e: + print(f"ERROR: Failed to download {template} System VM template: {e}") + +def collect_template_metadata(selected_templates, options): + template_metadata_list = [] + if not os.path.exists(SYSTEMVM_TEMPLATES_METADATA_FILE): + print(f"ERROR: System VM templates metadata file not found at {SYSTEMVM_TEMPLATES_METADATA_FILE}, cannot download templates.") + sys.exit(1) + config = configparser.ConfigParser() + config.read(SYSTEMVM_TEMPLATES_METADATA_FILE) + template_repo_url = None + if options.systemvm_templates_repository: + if "default" in config and "downloadrepository" in config["default"]: + template_repo_url = config["default"]["downloadrepository"].strip() + if not template_repo_url: + print("ERROR: downloadrepository value is empty in metadata file, cannot use --systemvm-template-repository option.") + sys.exit(1) + for template in selected_templates: + if template in config: + url = config[template].get("downloadurl") + filename = config[template].get("filename") + checksum = config[template].get("checksum") + if url and filename: + if template_repo_url: + url = url.replace(template_repo_url, options.systemvm_templates_repository) + template_metadata_list.append({ + "template": template, + "url": url, + "filename": filename, + "checksum": checksum + }) + else: + print(f"ERROR: URL or filename not found for {template} System VM template in metadata.") + sys.exit(1) + else: + print(f"ERROR: No metadata found for {template} System VM template.") + sys.exit(1) + return template_metadata_list if __name__ == '__main__': initLoging("@MSLOGDIR@/setupManagement.log") @@ -45,6 +145,16 @@ if __name__ == '__main__': parser.add_option("--https", action="store_true", dest="https", help="Enable HTTPs connection of management server") parser.add_option("--tomcat7", action="store_true", dest="tomcat7", help="Depreciated option, don't use it") parser.add_option("--no-start", action="store_true", dest="nostart", help="Do not start management server after successful configuration") + parser.add_option( + "--systemvm-templates", + dest="systemvm_templates", + help="Specify System VM templates to download: all, kvm-aarch64, kvm-x86_64, xenserver, vmware or comma-separated list of hypervisor combinations (e.g., kvm-x86_64,xenserver). Default is kvm-x86_64.", + ) + parser.add_option( + "--systemvm-templates-repository", + dest="systemvm_templates_repository", + help="Specify the URL to download System VM templates from." + ) (options, args) = parser.parse_args() if options.https: glbEnv.svrMode = "HttpsServer" @@ -53,6 +163,34 @@ if __name__ == '__main__': if options.nostart: glbEnv.noStart = True + available_templates = ["kvm-aarch64", "kvm-x86_64", "xenserver", "vmware"] + templates_arg = options.systemvm_templates + + selected_templates = ["kvm-x86_64"] + if templates_arg: + templates_list = [t.strip().lower() for t in templates_arg.split(",")] + if "all" in templates_list: + if len(templates_list) > 1: + print("WARNING: 'all' specified for System VM templates, ignoring other specified templates.") + selected_templates = available_templates + else: + invalid_templates = [] + for t in templates_list: + if t in available_templates: + if t not in selected_templates: + selected_templates.append(t) + else: + if t not in invalid_templates: + invalid_templates.append(t) + if invalid_templates: + print(f"ERROR: Invalid System VM template names provided: {', '.join(invalid_templates)}") + sys.exit(1) + print(f"Selected systemvm templates to download: {', '.join(selected_templates) if selected_templates else 'None'}") + + template_metadata_list = [] + if selected_templates: + template_metadata_list = collect_template_metadata(selected_templates, options) + glbEnv.mode = "Server" print("Starting to configure CloudStack Management Server:") @@ -74,3 +212,6 @@ if __name__ == '__main__': syscfg.restore() except: pass + + for meta in template_metadata_list: + download_template_if_needed(meta["template"], meta["url"], meta["filename"], meta["checksum"]) diff --git a/client/bindir/cloud-update-xenserver-licenses.in b/client/bindir/cloud-update-xenserver-licenses.in index 2be3a083f973..fca48f9eafbb 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") - -#------------------ 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 splitlast(string,splitter): - splitted = string.split(splitter) - first,last = splitter.join(splitted[:-1]),splitted[-1] - return first,last +parser.add_option( + "-a", + "--all", + action="store_true", + dest="all", + default=False, + help="deploy to all known hosts rather that a single host", +) + +# ------------------ 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 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 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 - - 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 -------------------- + 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" + + 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, + ) + + +# ------------- 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: + 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") - -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) + 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") + +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 5958486b4dff..7c5e3f925b08 100644 --- a/client/conf/server.properties.in +++ b/client/conf/server.properties.in @@ -62,3 +62,8 @@ extensions.deployment.mode=@EXTENSIONSDEPLOYMENTMODE@ # Thread pool configuration #threads.min=10 #threads.max=500 + +# The URL prefix for the system VM templates repository. When downloading system VM templates, the server replaces the +# `downloadrepository` key value from the metadata file in template URLs. If not specified, the original template URL +# will be used for download. +# system.vm.templates.download.repository=http://download.cloudstack.org/systemvm/ diff --git a/client/pom.xml b/client/pom.xml index 605487404a87..968f735af600 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.22.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT @@ -121,6 +121,11 @@ cloud-plugin-storage-volume-adaptive ${project.version} + + org.apache.cloudstack + cloud-plugin-storage-volume-ontap + ${project.version} + org.apache.cloudstack cloud-plugin-storage-volume-solidfire @@ -251,6 +256,16 @@ cloud-plugin-metrics ${project.version} + + org.apache.cloudstack + cloud-plugin-kms-database + ${project.version} + + + org.apache.cloudstack + cloud-plugin-kms-pkcs11 + ${project.version} + org.apache.cloudstack cloud-plugin-network-nvp @@ -372,11 +387,6 @@ cloud-plugin-explicit-dedication ${project.version} - - org.apache.cloudstack - cloud-plugin-host-allocator-random - ${project.version} - org.apache.cloudstack cloud-plugin-outofbandmanagement-driver-ipmitool @@ -612,6 +622,16 @@ cloud-plugin-backup-nas ${project.version} + + org.apache.cloudstack + cloud-plugin-integrations-veeam-control-service + ${project.version} + + + org.apache.cloudstack + cloud-plugin-backup-kvm-backup-on-secondary-storage + ${project.version} + org.apache.cloudstack cloud-plugin-integrations-kubernetes-service @@ -662,20 +682,32 @@ cloud-utils ${project.version} + + org.apache.cloudstack + cloud-plugin-dns-powerdns + ${project.version} + + - ru.concerteza.buildnumber - maven-jgit-buildnumber-plugin - 1.2.6 + org.codehaus.mojo + buildnumber-maven-plugin + 3.2.0 git-buildnumber - extract-buildnumber + create prepare-package + + false + false + true + unknown + @@ -688,11 +720,11 @@ org.apache.cloudstack.ServerDaemon - ${git.branch} - ${git.tag} - ${git.revision} - ${git.revision} - ${git.branch} + ${scmBranch} + ${project.version} + ${buildNumber} + ${buildNumber} + ${scmBranch} @@ -710,17 +742,17 @@
org.bouncycastle - bcprov-jdk15on + bcprov-jdk18on ${cs.bcprov.version} org.bouncycastle - bcpkix-jdk15on + bcpkix-jdk18on ${cs.bcprov.version} org.bouncycastle - bctls-jdk15on + bctls-jdk18on ${cs.bcprov.version}
@@ -900,13 +932,13 @@ org.bouncycastle - bcprov-jdk15on + bcprov-jdk18on false ${project.build.directory}/lib org.bouncycastle - bcpkix-jdk15on + bcpkix-jdk18on false ${project.build.directory}/lib @@ -930,7 +962,7 @@ org.bouncycastle - bctls-jdk15on + bctls-jdk18on false ${project.build.directory}/lib @@ -965,9 +997,9 @@ org.apache.tomcat.embed:tomcat-embed-core org.apache.geronimo.specs:geronimo-servlet_3.0_spec org.apache.geronimo.specs:geronimo-javamail_1.4_spec - org.bouncycastle:bcprov-jdk15on - org.bouncycastle:bcpkix-jdk15on - org.bouncycastle:bctls-jdk15on + org.bouncycastle:bcprov-jdk18on + org.bouncycastle:bcpkix-jdk18on + org.bouncycastle:bctls-jdk18on com.mysql:mysql-connector-j org.apache.cloudstack:cloud-plugin-storage-volume-storpool org.apache.cloudstack:cloud-plugin-storage-volume-linstor diff --git a/client/src/main/java/org/apache/cloudstack/ACSRequestLog.java b/client/src/main/java/org/apache/cloudstack/ACSRequestLog.java index 123d2761e008..249451120f1c 100644 --- a/client/src/main/java/org/apache/cloudstack/ACSRequestLog.java +++ b/client/src/main/java/org/apache/cloudstack/ACSRequestLog.java @@ -18,6 +18,7 @@ // package org.apache.cloudstack; +import com.cloud.api.ApiServlet; import com.cloud.utils.StringUtils; import org.eclipse.jetty.server.NCSARequestLog; import org.eclipse.jetty.server.Request; @@ -25,6 +26,7 @@ import org.eclipse.jetty.util.DateCache; import org.eclipse.jetty.util.component.LifeCycle; +import java.net.InetAddress; import java.util.Locale; import java.util.TimeZone; @@ -51,9 +53,8 @@ public void log(Request request, Response response) { StringBuilder sb = buffers.get(); sb.setLength(0); - sb.append(request.getHttpChannel().getEndPoint() - .getRemoteAddress().getAddress() - .getHostAddress()) + InetAddress remoteAddress = ApiServlet.getClientAddress(request); + sb.append(remoteAddress.getHostAddress()) .append(" - - [") .append(dateCache.format(request.getTimeStamp())) .append("] \"") diff --git a/client/src/main/java/org/apache/cloudstack/ServerDaemon.java b/client/src/main/java/org/apache/cloudstack/ServerDaemon.java index 196695e1fc6b..06477fff8986 100644 --- a/client/src/main/java/org/apache/cloudstack/ServerDaemon.java +++ b/client/src/main/java/org/apache/cloudstack/ServerDaemon.java @@ -24,15 +24,12 @@ 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.RequestLog; @@ -74,11 +71,6 @@ public class ServerDaemon implements Daemon { private static final String BIND_INTERFACE = "bind.interface"; private static final String CONTEXT_PATH = "context.path"; private static final String SESSION_TIMEOUT = "session.timeout"; - private static final String HTTP_ENABLE = "http.enable"; - private static final String HTTP_PORT = "http.port"; - private static final String HTTPS_ENABLE = "https.enable"; - private static final String HTTPS_PORT = "https.port"; - private static final String KEYSTORE_FILE = "https.keystore"; private static final String KEYSTORE_PASSWORD = "https.keystore.password"; private static final String WEBAPP_DIR = "webapp.dir"; private static final String ACCESS_LOG = "access.log"; @@ -140,11 +132,11 @@ public void init(final DaemonContext context) { } setBindInterface(properties.getProperty(BIND_INTERFACE, null)); setContextPath(properties.getProperty(CONTEXT_PATH, "/client")); - setHttpEnable(Boolean.valueOf(properties.getProperty(HTTP_ENABLE, "true"))); - setHttpPort(Integer.valueOf(properties.getProperty(HTTP_PORT, "8080"))); - setHttpsEnable(Boolean.valueOf(properties.getProperty(HTTPS_ENABLE, "false"))); - setHttpsPort(Integer.valueOf(properties.getProperty(HTTPS_PORT, "8443"))); - setKeystoreFile(properties.getProperty(KEYSTORE_FILE)); + setHttpEnable(Boolean.valueOf(properties.getProperty(ServerProperties.HTTP_ENABLE, "true"))); + setHttpPort(Integer.valueOf(properties.getProperty(ServerProperties.HTTP_PORT, "8080"))); + setHttpsEnable(Boolean.valueOf(properties.getProperty(ServerProperties.HTTPS_ENABLE, "false"))); + setHttpsPort(Integer.valueOf(properties.getProperty(ServerProperties.HTTPS_PORT, "8443"))); + setKeystoreFile(properties.getProperty(ServerProperties.KEYSTORE_FILE)); setKeystorePassword(properties.getProperty(KEYSTORE_PASSWORD)); setWebAppLocation(properties.getProperty(WEBAPP_DIR)); setAccessLogFile(properties.getProperty(ACCESS_LOG, "access.log")); @@ -193,7 +185,6 @@ public void start() throws Exception { httpConfig.setResponseHeaderSize(8192); httpConfig.setSendServerVersion(false); httpConfig.setSendDateHeader(false); - addForwardingCustomiser(httpConfig); // HTTP Connector createHttpConnector(httpConfig); @@ -216,21 +207,6 @@ public void start() throws Exception { 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(); diff --git a/cloud-cli/bindir/cloud-tool b/cloud-cli/bindir/cloud-tool index 410681a0dd5d..0f0815307a13 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 e4cd84357d1b..f91a330b7ba9 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.22.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT diff --git a/core/src/main/java/com/cloud/agent/api/CheckConvertInstanceCommand.java b/core/src/main/java/com/cloud/agent/api/CheckConvertInstanceCommand.java index fc066e5c5899..c46fb697a3c1 100644 --- a/core/src/main/java/com/cloud/agent/api/CheckConvertInstanceCommand.java +++ b/core/src/main/java/com/cloud/agent/api/CheckConvertInstanceCommand.java @@ -18,6 +18,8 @@ public class CheckConvertInstanceCommand extends Command { boolean checkWindowsGuestConversionSupport = false; + boolean useVddk = false; + String vddkLibDir; public CheckConvertInstanceCommand() { } @@ -26,6 +28,11 @@ public CheckConvertInstanceCommand(boolean checkWindowsGuestConversionSupport) { this.checkWindowsGuestConversionSupport = checkWindowsGuestConversionSupport; } + public CheckConvertInstanceCommand(boolean checkWindowsGuestConversionSupport, boolean useVddk) { + this.checkWindowsGuestConversionSupport = checkWindowsGuestConversionSupport; + this.useVddk = useVddk; + } + @Override public boolean executeInSequence() { return false; @@ -34,4 +41,20 @@ public boolean executeInSequence() { public boolean getCheckWindowsGuestConversionSupport() { return checkWindowsGuestConversionSupport; } + + public boolean isUseVddk() { + return useVddk; + } + + public void setUseVddk(boolean useVddk) { + this.useVddk = useVddk; + } + + public String getVddkLibDir() { + return vddkLibDir; + } + + public void setVddkLibDir(String vddkLibDir) { + this.vddkLibDir = vddkLibDir; + } } diff --git a/core/src/main/java/com/cloud/agent/api/CheckOnHostAnswer.java b/core/src/main/java/com/cloud/agent/api/CheckOnHostAnswer.java index 5a26b22ec6a5..41e266784db8 100644 --- a/core/src/main/java/com/cloud/agent/api/CheckOnHostAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/CheckOnHostAnswer.java @@ -38,6 +38,8 @@ public CheckOnHostAnswer(CheckOnHostCommand cmd, Boolean alive, String details) public CheckOnHostAnswer(CheckOnHostCommand cmd, String details) { super(cmd, false, details); + determined = false; + alive = false; } public boolean isDetermined() { @@ -47,5 +49,4 @@ public boolean isDetermined() { public boolean isAlive() { return alive; } - } diff --git a/core/src/main/java/com/cloud/agent/api/CheckOnHostCommand.java b/core/src/main/java/com/cloud/agent/api/CheckOnHostCommand.java index 94239f2900ef..72b5217604dc 100644 --- a/core/src/main/java/com/cloud/agent/api/CheckOnHostCommand.java +++ b/core/src/main/java/com/cloud/agent/api/CheckOnHostCommand.java @@ -24,7 +24,7 @@ public class CheckOnHostCommand extends Command { HostTO host; - boolean reportCheckFailureIfOneStorageIsDown; + boolean reportIfHeartBeatFailedForOneStoragePool; protected CheckOnHostCommand() { } @@ -34,17 +34,17 @@ public CheckOnHostCommand(Host host) { setWait(20); } - public CheckOnHostCommand(Host host, boolean reportCheckFailureIfOneStorageIsDown) { + public CheckOnHostCommand(Host host, boolean reportIfHeartBeatFailedForOneStoragePool) { this(host); - this.reportCheckFailureIfOneStorageIsDown = reportCheckFailureIfOneStorageIsDown; + this.reportIfHeartBeatFailedForOneStoragePool = reportIfHeartBeatFailedForOneStoragePool; } public HostTO getHost() { return host; } - public boolean isCheckFailedOnOneStorage() { - return reportCheckFailureIfOneStorageIsDown; + public boolean shouldReportIfHeartBeatFailedForOneStoragePool() { + return reportIfHeartBeatFailedForOneStoragePool; } @Override diff --git a/core/src/main/java/com/cloud/agent/api/CleanupConvertedInstanceDisksCommand.java b/core/src/main/java/com/cloud/agent/api/CleanupConvertedInstanceDisksCommand.java new file mode 100644 index 000000000000..00373ec75364 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/CleanupConvertedInstanceDisksCommand.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; + +import com.cloud.agent.api.to.DataStoreTO; + +/** + * This command is used to cleanup the converted instance disks from the storage pool: vmVolumesStore and the prefix: vmVolumesPrefix. + */ +public class CleanupConvertedInstanceDisksCommand extends Command { + + private DataStoreTO vmVolumesStore; + private String vmVolumesPrefix; + + public CleanupConvertedInstanceDisksCommand(DataStoreTO vmVolumesStore, String vmVolumesPrefix) { + this.vmVolumesStore = vmVolumesStore; + this.vmVolumesPrefix = vmVolumesPrefix; + } + + public DataStoreTO getVmVolumesStore() { + return vmVolumesStore; + } + + public void setVmVolumesStore(DataStoreTO vmVolumesStore) { + this.vmVolumesStore = vmVolumesStore; + } + + public String getVmVolumesPrefix() { + return vmVolumesPrefix; + } + + public void setVmVolumesPrefix(String vmVolumesPrefix) { + this.vmVolumesPrefix = vmVolumesPrefix; + } + + @Override + public boolean executeInSequence() { + return false; + } +} 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 f938d0ac55d7..38e0dca7736c 100644 --- a/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java +++ b/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java @@ -23,30 +23,41 @@ public class ConvertInstanceCommand extends Command { private RemoteInstanceTO sourceInstance; + private String originalVMName; private Hypervisor.HypervisorType destinationHypervisorType; private DataStoreTO conversionTemporaryLocation; private String templateDirOnConversionLocation; private boolean checkConversionSupport; private boolean exportOvfToConversionLocation; private int threadsCountToExportOvf = 0; + private String extraParams; + private boolean useVddk; + private String vddkLibDir; + private String vddkTransports; + private String vddkThumbprint; public ConvertInstanceCommand() { } public ConvertInstanceCommand(RemoteInstanceTO sourceInstance, Hypervisor.HypervisorType destinationHypervisorType, DataStoreTO conversionTemporaryLocation, - String templateDirOnConversionLocation, boolean checkConversionSupport, boolean exportOvfToConversionLocation) { + String templateDirOnConversionLocation, boolean checkConversionSupport, boolean exportOvfToConversionLocation, String sourceVMName) { this.sourceInstance = sourceInstance; this.destinationHypervisorType = destinationHypervisorType; this.conversionTemporaryLocation = conversionTemporaryLocation; this.templateDirOnConversionLocation = templateDirOnConversionLocation; this.checkConversionSupport = checkConversionSupport; this.exportOvfToConversionLocation = exportOvfToConversionLocation; + this.originalVMName = sourceVMName; } public RemoteInstanceTO getSourceInstance() { return sourceInstance; } + public String getOriginalVMName() { + return originalVMName; + } + public Hypervisor.HypervisorType getDestinationHypervisorType() { return destinationHypervisorType; } @@ -75,6 +86,46 @@ public void setThreadsCountToExportOvf(int threadsCountToExportOvf) { this.threadsCountToExportOvf = threadsCountToExportOvf; } + public String getExtraParams() { + return extraParams; + } + + public void setExtraParams(String extraParams) { + this.extraParams = extraParams; + } + + public boolean isUseVddk() { + return useVddk; + } + + public void setUseVddk(boolean useVddk) { + this.useVddk = useVddk; + } + + public String getVddkLibDir() { + return vddkLibDir; + } + + public void setVddkLibDir(String vddkLibDir) { + this.vddkLibDir = vddkLibDir; + } + + public String getVddkTransports() { + return vddkTransports; + } + + public void setVddkTransports(String vddkTransports) { + this.vddkTransports = vddkTransports; + } + + public String getVddkThumbprint() { + return vddkThumbprint; + } + + public void setVddkThumbprint(String vddkThumbprint) { + this.vddkThumbprint = vddkThumbprint; + } + @Override public boolean executeInSequence() { return false; diff --git a/core/src/main/java/com/cloud/agent/api/GetExternalConsoleAnswer.java b/core/src/main/java/com/cloud/agent/api/GetExternalConsoleAnswer.java new file mode 100644 index 000000000000..e913d6f0d3a1 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/GetExternalConsoleAnswer.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 com.cloud.agent.api; + +public class GetExternalConsoleAnswer extends Answer { + + private String url; + private String host; + private Integer port; + @LogLevel(LogLevel.Log4jLevel.Off) + private String password; + private String protocol; + private boolean passwordOneTimeUseOnly; + + public GetExternalConsoleAnswer(Command command, String details) { + super(command, false, details); + } + + public GetExternalConsoleAnswer(Command command, String url, String host, Integer port, String password, + boolean passwordOneTimeUseOnly, String protocol) { + super(command, true, ""); + this.url = url; + this.host = host; + this.port = port; + this.password = password; + this.passwordOneTimeUseOnly = passwordOneTimeUseOnly; + this.protocol = protocol; + } + + public String getUrl() { + return url; + } + + public String getHost() { + return host; + } + + public Integer getPort() { + return port; + } + + public String getPassword() { + return password; + } + + public String getProtocol() { + return protocol; + } + + public boolean isPasswordOneTimeUseOnly() { + return passwordOneTimeUseOnly; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/GetExternalConsoleCommand.java b/core/src/main/java/com/cloud/agent/api/GetExternalConsoleCommand.java new file mode 100644 index 000000000000..fc2134f631fb --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/GetExternalConsoleCommand.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 com.cloud.agent.api; + +import com.cloud.agent.api.to.VirtualMachineTO; + +public class GetExternalConsoleCommand extends Command { + String vmName; + VirtualMachineTO vm; + protected boolean executeInSequence; + + public GetExternalConsoleCommand(String vmName, VirtualMachineTO vm) { + this.vmName = vmName; + this.vm = vm; + this.executeInSequence = false; + } + + public String getVmName() { + return this.vmName; + } + + public void setVirtualMachine(VirtualMachineTO vm) { + this.vm = vm; + } + + public VirtualMachineTO getVirtualMachine() { + return vm; + } + + @Override + public boolean executeInSequence() { + return executeInSequence; + } + + public void setExecuteInSequence(boolean executeInSequence) { + this.executeInSequence = executeInSequence; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/ImportConvertedInstanceCommand.java b/core/src/main/java/com/cloud/agent/api/ImportConvertedInstanceCommand.java index 9d50e852ced4..eadfa6556f86 100644 --- a/core/src/main/java/com/cloud/agent/api/ImportConvertedInstanceCommand.java +++ b/core/src/main/java/com/cloud/agent/api/ImportConvertedInstanceCommand.java @@ -27,17 +27,20 @@ public class ImportConvertedInstanceCommand extends Command { private List destinationStoragePools; private DataStoreTO conversionTemporaryLocation; private String temporaryConvertUuid; + private boolean forceConvertToPool; public ImportConvertedInstanceCommand() { } public ImportConvertedInstanceCommand(RemoteInstanceTO sourceInstance, List destinationStoragePools, - DataStoreTO conversionTemporaryLocation, String temporaryConvertUuid) { + DataStoreTO conversionTemporaryLocation, String temporaryConvertUuid, + boolean forceConvertToPool) { this.sourceInstance = sourceInstance; this.destinationStoragePools = destinationStoragePools; this.conversionTemporaryLocation = conversionTemporaryLocation; this.temporaryConvertUuid = temporaryConvertUuid; + this.forceConvertToPool = forceConvertToPool; } public RemoteInstanceTO getSourceInstance() { @@ -56,6 +59,10 @@ public String getTemporaryConvertUuid() { return temporaryConvertUuid; } + public boolean isForceConvertToPool() { + return forceConvertToPool; + } + @Override public boolean executeInSequence() { return false; diff --git a/core/src/main/java/com/cloud/agent/api/MigrateBackupsBetweenSecondaryStoragesCommand.java b/core/src/main/java/com/cloud/agent/api/MigrateBackupsBetweenSecondaryStoragesCommand.java new file mode 100644 index 000000000000..7794b4a53083 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/MigrateBackupsBetweenSecondaryStoragesCommand.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; + +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; + +import java.util.List; + +public class MigrateBackupsBetweenSecondaryStoragesCommand extends MigrateBetweenSecondaryStoragesCommand { + + List> backupChain; + + public MigrateBackupsBetweenSecondaryStoragesCommand() { + } + + public MigrateBackupsBetweenSecondaryStoragesCommand(List> backupChain, DataStoreTO srcDataStore, DataStoreTO destDataStore) { + super(srcDataStore, destDataStore); + this.backupChain = backupChain; + } + + public List> getBackupChain() { + return backupChain; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/MigrateBetweenSecondaryStoragesCommand.java b/core/src/main/java/com/cloud/agent/api/MigrateBetweenSecondaryStoragesCommand.java new file mode 100644 index 000000000000..48dc7e2c85f4 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/MigrateBetweenSecondaryStoragesCommand.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 com.cloud.agent.api; + +import com.cloud.agent.api.to.DataStoreTO; + +public abstract class MigrateBetweenSecondaryStoragesCommand extends Command { + + DataStoreTO srcDataStore; + DataStoreTO destDataStore; + + public MigrateBetweenSecondaryStoragesCommand() { + } + + public MigrateBetweenSecondaryStoragesCommand(DataStoreTO srcDataStore, DataStoreTO destDataStore) { + this.srcDataStore = srcDataStore; + this.destDataStore = destDataStore; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public DataStoreTO getSrcDataStore() { + return srcDataStore; + } + + public DataStoreTO getDestDataStore() { + return destDataStore; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/MigrateBetweenSecondaryStoragesCommandAnswer.java b/core/src/main/java/com/cloud/agent/api/MigrateBetweenSecondaryStoragesCommandAnswer.java new file mode 100644 index 000000000000..fd303093e092 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/MigrateBetweenSecondaryStoragesCommandAnswer.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; + +import com.cloud.utils.Pair; + +import java.util.List; + +public class MigrateBetweenSecondaryStoragesCommandAnswer extends Answer { + + List> migratedResourcesIdAndCheckpointPath; + + public MigrateBetweenSecondaryStoragesCommandAnswer() { + } + + public MigrateBetweenSecondaryStoragesCommandAnswer(MigrateBetweenSecondaryStoragesCommand cmd, boolean success, String result, List> migratedResourcesIdAndCheckpointPath) { + super(cmd, success, result); + this.migratedResourcesIdAndCheckpointPath = migratedResourcesIdAndCheckpointPath; + } + + public List> getMigratedResources() { + return migratedResourcesIdAndCheckpointPath; + } +} 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 5ac4e9ae445e..7196247ffc23 100644 --- a/core/src/main/java/com/cloud/agent/api/MigrateCommand.java +++ b/core/src/main/java/com/cloud/agent/api/MigrateCommand.java @@ -26,6 +26,7 @@ import com.cloud.agent.api.to.DpdkTO; import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.storage.Storage; public class MigrateCommand extends Command { private String vmName; @@ -42,6 +43,7 @@ public class MigrateCommand extends Command { private Map dpdkInterfaceMapping = new HashMap<>(); private int newVmCpuShares; + private boolean clvmCrossPoolMigration; Map vlanToPersistenceMap = new HashMap<>(); @@ -149,6 +151,14 @@ public void setNewVmCpuShares(int newVmCpuShares) { this.newVmCpuShares = newVmCpuShares; } + public boolean isClvmCrossPoolMigration() { + return clvmCrossPoolMigration; + } + + public void setClvmCrossPoolMigration(boolean clvmCrossPoolMigration) { + this.clvmCrossPoolMigration = clvmCrossPoolMigration; + } + public static class MigrateDiskInfo { public enum DiskType { FILE, BLOCK; @@ -184,6 +194,8 @@ public String toString() { private final String sourceText; private final String backingStoreText; private boolean isSourceDiskOnStorageFileSystem; + private Storage.StoragePoolType sourcePoolType; + private Storage.StoragePoolType destPoolType; public MigrateDiskInfo(final String serialNumber, final DiskType diskType, final DriverType driverType, final Source source, final String sourceText) { this.serialNumber = serialNumber; @@ -232,6 +244,22 @@ public boolean isSourceDiskOnStorageFileSystem() { public void setSourceDiskOnStorageFileSystem(boolean isDiskOnFileSystemStorage) { this.isSourceDiskOnStorageFileSystem = isDiskOnFileSystemStorage; } + + public Storage.StoragePoolType getSourcePoolType() { + return sourcePoolType; + } + + public void setSourcePoolType(Storage.StoragePoolType sourcePoolType) { + this.sourcePoolType = sourcePoolType; + } + + public Storage.StoragePoolType getDestPoolType() { + return destPoolType; + } + + public void setDestPoolType(Storage.StoragePoolType destPoolType) { + this.destPoolType = destPoolType; + } } @Override 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 a3401d6faed7..275468214f51 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 ModifyStoragePoolAnswer(ModifyStoragePoolCommand cmd, long capacityBytes, templateInfo = tInfo; } + public ModifyStoragePoolAnswer(final Command command, final boolean success, final String details) { + super(command, success, details); + } + public ModifyStoragePoolAnswer(ModifyStoragePoolCommand cmd, boolean success, String details) { super(cmd, success, details); } diff --git a/core/src/main/java/com/cloud/agent/api/PostMigrationAnswer.java b/core/src/main/java/com/cloud/agent/api/PostMigrationAnswer.java new file mode 100644 index 000000000000..24fdf8402029 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/PostMigrationAnswer.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; + +/** + * Answer for PostMigrationCommand. + * Indicates success or failure of post-migration operations on the destination host. + */ +public class PostMigrationAnswer extends Answer { + + protected PostMigrationAnswer() { + } + + public PostMigrationAnswer(PostMigrationCommand cmd, String detail) { + super(cmd, false, detail); + } + + public PostMigrationAnswer(PostMigrationCommand cmd, Exception ex) { + super(cmd, ex); + } + + public PostMigrationAnswer(PostMigrationCommand cmd) { + super(cmd, true, null); + } +} diff --git a/core/src/main/java/com/cloud/agent/api/PostMigrationCommand.java b/core/src/main/java/com/cloud/agent/api/PostMigrationCommand.java new file mode 100644 index 000000000000..938000c3593c --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/PostMigrationCommand.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 com.cloud.agent.api.to.VirtualMachineTO; + +/** + * PostMigrationCommand is sent to the destination host after a successful VM migration. + * It performs post-migration tasks such as: + * - Claiming exclusive locks on CLVM volumes (converting from shared to exclusive mode) + * - Other post-migration cleanup operations + */ +public class PostMigrationCommand extends Command { + private VirtualMachineTO vm; + private String vmName; + + protected PostMigrationCommand() { + } + + public PostMigrationCommand(VirtualMachineTO vm, String vmName) { + this.vm = vm; + this.vmName = vmName; + } + + public VirtualMachineTO getVirtualMachine() { + return vm; + } + + public String getVmName() { + return vmName; + } + + @Override + public boolean executeInSequence() { + return true; + } + + @Override + public boolean isBypassHostMaintenance() { + return true; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/PreMigrationCommand.java b/core/src/main/java/com/cloud/agent/api/PreMigrationCommand.java new file mode 100644 index 000000000000..3ff30391eaef --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/PreMigrationCommand.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 com.cloud.agent.api; + +import com.cloud.agent.api.to.VirtualMachineTO; + +/** + * PreMigrationCommand is sent to the source host before VM migration starts. + * It performs pre-migration tasks such as: + * - Converting CLVM volume exclusive locks to shared mode so destination host can access them + * - Other pre-migration preparation operations on the source host + * + * This command runs on the SOURCE host before PrepareForMigrationCommand runs on the DESTINATION host. + */ +public class PreMigrationCommand extends Command { + private VirtualMachineTO vm; + private String vmName; + + protected PreMigrationCommand() { + } + + public PreMigrationCommand(VirtualMachineTO vm, String vmName) { + this.vm = vm; + this.vmName = vmName; + } + + public VirtualMachineTO getVirtualMachine() { + return vm; + } + + public String getVmName() { + return vmName; + } + + @Override + public boolean executeInSequence() { + return true; + } + + @Override + public boolean isBypassHostMaintenance() { + return true; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/PropagateResourceEventCommand.java b/core/src/main/java/com/cloud/agent/api/PropagateResourceEventCommand.java index ed337885beed..21c4e7b97d03 100644 --- a/core/src/main/java/com/cloud/agent/api/PropagateResourceEventCommand.java +++ b/core/src/main/java/com/cloud/agent/api/PropagateResourceEventCommand.java @@ -24,6 +24,8 @@ public class PropagateResourceEventCommand extends Command { long hostId; ResourceState.Event event; + boolean forced; + boolean forceDeleteStorage; protected PropagateResourceEventCommand() { @@ -34,6 +36,13 @@ public PropagateResourceEventCommand(long hostId, ResourceState.Event event) { this.event = event; } + public PropagateResourceEventCommand(long hostId, ResourceState.Event event, boolean forced, boolean forceDeleteStorage) { + this.hostId = hostId; + this.event = event; + this.forced = forced; + this.forceDeleteStorage = forceDeleteStorage; + } + public long getHostId() { return hostId; } @@ -42,6 +51,14 @@ public ResourceState.Event getEvent() { return event; } + public boolean isForced() { + return forced; + } + + public boolean isForceDeleteStorage() { + return forceDeleteStorage; + } + @Override public boolean executeInSequence() { // TODO Auto-generated method stub diff --git a/core/src/main/java/com/cloud/agent/api/RunCustomActionCommand.java b/core/src/main/java/com/cloud/agent/api/RunCustomActionCommand.java index 36489ad4fa5f..113073ac5ee5 100644 --- a/core/src/main/java/com/cloud/agent/api/RunCustomActionCommand.java +++ b/core/src/main/java/com/cloud/agent/api/RunCustomActionCommand.java @@ -21,10 +21,12 @@ import java.util.Map; +import com.cloud.agent.api.to.VirtualMachineTO; + public class RunCustomActionCommand extends Command { String actionName; - Long vmId; + VirtualMachineTO vmTO; Map parameters; public RunCustomActionCommand(String actionName) { @@ -36,12 +38,12 @@ public String getActionName() { return actionName; } - public Long getVmId() { - return vmId; + public VirtualMachineTO getVmTO() { + return vmTO; } - public void setVmId(Long vmId) { - this.vmId = vmId; + public void setVmTO(VirtualMachineTO vmTO) { + this.vmTO = vmTO; } public Map getParameters() { diff --git a/core/src/main/java/com/cloud/agent/api/ScaleVmCommand.java b/core/src/main/java/com/cloud/agent/api/ScaleVmCommand.java index dc63b1ee746d..e2953e9c7ad7 100644 --- a/core/src/main/java/com/cloud/agent/api/ScaleVmCommand.java +++ b/core/src/main/java/com/cloud/agent/api/ScaleVmCommand.java @@ -30,6 +30,7 @@ public class ScaleVmCommand extends Command { Integer maxSpeed; long minRam; long maxRam; + private boolean limitCpuUseChange; public VirtualMachineTO getVm() { return vm; @@ -43,7 +44,7 @@ public int getCpus() { return cpus; } - public ScaleVmCommand(String vmName, int cpus, Integer minSpeed, Integer maxSpeed, long minRam, long maxRam, boolean limitCpuUse) { + public ScaleVmCommand(String vmName, int cpus, Integer minSpeed, Integer maxSpeed, long minRam, long maxRam, boolean limitCpuUse, Double cpuQuotaPercentage, boolean limitCpuUseChange) { super(); this.vmName = vmName; this.cpus = cpus; @@ -52,6 +53,8 @@ public ScaleVmCommand(String vmName, int cpus, Integer minSpeed, Integer maxSpee this.minRam = minRam; this.maxRam = maxRam; this.vm = new VirtualMachineTO(1L, vmName, null, cpus, minSpeed, maxSpeed, minRam, maxRam, null, null, false, limitCpuUse, null); + this.vm.setCpuQuotaPercentage(cpuQuotaPercentage); + this.limitCpuUseChange = limitCpuUseChange; } public void setCpus(int cpus) { @@ -102,6 +105,10 @@ public VirtualMachineTO getVirtualMachine() { return vm; } + public boolean getLimitCpuUseChange() { + return limitCpuUseChange; + } + @Override public boolean executeInSequence() { return true; diff --git a/core/src/main/java/com/cloud/agent/api/StartCommand.java b/core/src/main/java/com/cloud/agent/api/StartCommand.java index 24b0ac3787b5..ea763d6cd4bd 100644 --- a/core/src/main/java/com/cloud/agent/api/StartCommand.java +++ b/core/src/main/java/com/cloud/agent/api/StartCommand.java @@ -22,13 +22,15 @@ import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.host.Host; +import java.util.List; + /** */ public class StartCommand extends Command { VirtualMachineTO vm; String hostIp; boolean executeInSequence = false; - String secondaryStorage; + private List secondaryStorages; public VirtualMachineTO getVirtualMachine() { return vm; @@ -50,18 +52,18 @@ public StartCommand(VirtualMachineTO vm, Host host, boolean executeInSequence) { this.vm = vm; this.hostIp = host.getPrivateIpAddress(); this.executeInSequence = executeInSequence; - this.secondaryStorage = null; + this.secondaryStorages = null; } public String getHostIp() { return this.hostIp; } - public String getSecondaryStorage() { - return this.secondaryStorage; + public List getSecondaryStorages() { + return this.secondaryStorages; } - public void setSecondaryStorage(String secondary) { - this.secondaryStorage = secondary; + public void setSecondaryStorages(List secondary) { + this.secondaryStorages = secondary; } } diff --git a/core/src/main/java/com/cloud/agent/api/UnmanageInstanceAnswer.java b/core/src/main/java/com/cloud/agent/api/UnmanageInstanceAnswer.java new file mode 100644 index 000000000000..39a35d499906 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/UnmanageInstanceAnswer.java @@ -0,0 +1,27 @@ +// +// 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 UnmanageInstanceAnswer extends Answer { + + public UnmanageInstanceAnswer(UnmanageInstanceCommand cmd, boolean success, String details) { + super(cmd, success, details); + } +} diff --git a/core/src/main/java/com/cloud/agent/api/UnmanageInstanceCommand.java b/core/src/main/java/com/cloud/agent/api/UnmanageInstanceCommand.java new file mode 100644 index 000000000000..dd504b9ea265 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/UnmanageInstanceCommand.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 com.cloud.agent.api; + +import com.cloud.agent.api.to.VirtualMachineTO; + +/** + */ +public class UnmanageInstanceCommand extends Command { + String instanceName; + boolean executeInSequence = false; + VirtualMachineTO vm; + boolean isConfigDriveAttached; + + @Override + public boolean executeInSequence() { + return executeInSequence; + } + + public UnmanageInstanceCommand(VirtualMachineTO vm) { + this.vm = vm; + this.instanceName = vm.getName(); + } + + public UnmanageInstanceCommand(String instanceName) { + this.instanceName = instanceName; + } + + public String getInstanceName() { + return instanceName; + } + + public VirtualMachineTO getVm() { + return vm; + } + + public boolean isConfigDriveAttached() { + return isConfigDriveAttached; + } + + public void setConfigDriveAttached(boolean configDriveAttached) { + isConfigDriveAttached = configDriveAttached; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/UpdateVmNicAnswer.java b/core/src/main/java/com/cloud/agent/api/UpdateVmNicAnswer.java new file mode 100644 index 000000000000..0af6d7fa3a3d --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/UpdateVmNicAnswer.java @@ -0,0 +1,27 @@ +// 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 UpdateVmNicAnswer extends Answer { + public UpdateVmNicAnswer() { + } + + public UpdateVmNicAnswer(UpdateVmNicCommand cmd, boolean success, String result) { + super(cmd, success, result); + } +} diff --git a/core/src/main/java/com/cloud/agent/api/UpdateVmNicCommand.java b/core/src/main/java/com/cloud/agent/api/UpdateVmNicCommand.java new file mode 100644 index 000000000000..a5c5faf32fed --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/UpdateVmNicCommand.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; + +public class UpdateVmNicCommand extends Command { + + String nicMacAddress; + String instanceName; + Boolean enabled; + + @Override + public boolean executeInSequence() { + return true; + } + + protected UpdateVmNicCommand() { + } + + public UpdateVmNicCommand(String nicMacAdderss, String instanceName, Boolean enabled) { + this.nicMacAddress = nicMacAdderss; + this.instanceName = instanceName; + this.enabled = enabled; + } + + public String getNicMacAddress() { + return nicMacAddress; + } + + public String getVmName() { + return instanceName; + } + + public Boolean isEnabled() { + return enabled; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/routing/DhcpEntryCommand.java b/core/src/main/java/com/cloud/agent/api/routing/DhcpEntryCommand.java index 7fb65fe15cf9..67ca447e3853 100644 --- a/core/src/main/java/com/cloud/agent/api/routing/DhcpEntryCommand.java +++ b/core/src/main/java/com/cloud/agent/api/routing/DhcpEntryCommand.java @@ -36,6 +36,7 @@ public class DhcpEntryCommand extends NetworkElementCommand { private boolean isDefault; boolean executeInSequence = false; boolean remove; + Long leaseTime; public boolean isRemove() { return remove; @@ -152,4 +153,12 @@ public boolean isDefault() { public void setDefault(boolean isDefault) { this.isDefault = isDefault; } + + public Long getLeaseTime() { + return leaseTime; + } + + public void setLeaseTime(Long leaseTime) { + this.leaseTime = leaseTime; + } } diff --git a/core/src/main/java/com/cloud/agent/api/routing/LoadBalancerConfigCommand.java b/core/src/main/java/com/cloud/agent/api/routing/LoadBalancerConfigCommand.java index d8cc74817d7b..96d73e11990d 100644 --- a/core/src/main/java/com/cloud/agent/api/routing/LoadBalancerConfigCommand.java +++ b/core/src/main/java/com/cloud/agent/api/routing/LoadBalancerConfigCommand.java @@ -36,6 +36,7 @@ public class LoadBalancerConfigCommand extends NetworkElementCommand { public String lbStatsAuth = "admin1:AdMiN123"; public String lbStatsUri = "/admin?stats"; public String maxconn = ""; + public Long idleTimeout = 50000L; /* 0=infinite, >0 = timeout in milliseconds */ public String lbProtocol; public boolean keepAliveEnabled = false; NicTO nic; @@ -50,7 +51,7 @@ public LoadBalancerConfigCommand(LoadBalancerTO[] loadBalancers, Long vpcId) { } public LoadBalancerConfigCommand(LoadBalancerTO[] loadBalancers, String publicIp, String guestIp, String privateIp, NicTO nic, Long vpcId, String maxconn, - boolean keepAliveEnabled) { + boolean keepAliveEnabled, Long idleTimeout) { this.loadBalancers = loadBalancers; this.lbStatsPublicIP = publicIp; this.lbStatsPrivateIP = privateIp; @@ -59,6 +60,7 @@ public LoadBalancerConfigCommand(LoadBalancerTO[] loadBalancers, String publicIp this.vpcId = vpcId; this.maxconn = maxconn; this.keepAliveEnabled = keepAliveEnabled; + this.idleTimeout = idleTimeout; } public NicTO getNic() { diff --git a/core/src/main/java/com/cloud/agent/api/routing/NetworkElementCommand.java b/core/src/main/java/com/cloud/agent/api/routing/NetworkElementCommand.java index 400b6bb80917..20d2ea0a443d 100644 --- a/core/src/main/java/com/cloud/agent/api/routing/NetworkElementCommand.java +++ b/core/src/main/java/com/cloud/agent/api/routing/NetworkElementCommand.java @@ -37,6 +37,7 @@ public abstract class NetworkElementCommand extends Command { public static final String ZONE_NETWORK_TYPE = "zone.network.type"; public static final String GUEST_BRIDGE = "guest.bridge"; public static final String VPC_PRIVATE_GATEWAY = "vpc.gateway.private"; + public static final String VPC_ID = "vpc.id"; public static final String FIREWALL_EGRESS_DEFAULT = "firewall.egress.default"; public static final String NETWORK_PUB_LAST_IP = "network.public.last.ip"; public static final String HYPERVISOR_HOST_PRIVATE_IP = "hypervisor.private.ip"; diff --git a/core/src/main/java/com/cloud/agent/api/routing/SetFirewallRulesCommand.java b/core/src/main/java/com/cloud/agent/api/routing/SetFirewallRulesCommand.java index c56f8d20fbe6..ff81ab7749c5 100644 --- a/core/src/main/java/com/cloud/agent/api/routing/SetFirewallRulesCommand.java +++ b/core/src/main/java/com/cloud/agent/api/routing/SetFirewallRulesCommand.java @@ -32,6 +32,7 @@ */ public class SetFirewallRulesCommand extends NetworkElementCommand { FirewallRuleTO[] rules; + Long vpcId; protected SetFirewallRulesCommand() { } @@ -40,10 +41,19 @@ public SetFirewallRulesCommand(List rules) { this.rules = rules.toArray(new FirewallRuleTO[rules.size()]); } + public SetFirewallRulesCommand(List rules, Long vpcId) { + this.rules = rules.toArray(new FirewallRuleTO[rules.size()]); + this.vpcId = vpcId; + } + public FirewallRuleTO[] getRules() { return rules; } + public Long getVpcId() { + return vpcId; + } + public String[][] generateFwRules() { String[][] result = new String[2][]; Set toAdd = new HashSet(); 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 index 4d61249c7cbc..9a571e34c7e6 100644 --- a/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotAnswer.java @@ -20,20 +20,19 @@ 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; + protected Map mapVolumeToSnapshotSize; - public CreateDiskOnlyVmSnapshotAnswer(Command command, boolean success, String details, Map> mapVolumeToSnapshotSizeAndNewVolumePath) { + public CreateDiskOnlyVmSnapshotAnswer(Command command, boolean success, String details, Map mapVolumeToSnapshotSize) { super(command, success, details); - this.mapVolumeToSnapshotSizeAndNewVolumePath = mapVolumeToSnapshotSizeAndNewVolumePath; + this.mapVolumeToSnapshotSize = mapVolumeToSnapshotSize; } - public Map> getMapVolumeToSnapshotSizeAndNewVolumePath() { - return mapVolumeToSnapshotSizeAndNewVolumePath; + public Map getMapVolumeToSnapshotSize() { + return mapVolumeToSnapshotSize; } } 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 index 952bf0c971de..da1b420625f8 100644 --- a/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotCommand.java +++ b/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotCommand.java @@ -21,6 +21,7 @@ import com.cloud.agent.api.VMSnapshotBaseCommand; import com.cloud.agent.api.VMSnapshotTO; +import com.cloud.utils.Pair; import com.cloud.vm.VirtualMachine; import org.apache.cloudstack.storage.to.VolumeObjectTO; @@ -30,12 +31,19 @@ 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); + List> volumeTosAndNewPaths; + + public CreateDiskOnlyVmSnapshotCommand(String vmName, VMSnapshotTO snapshot, List> volumeTosAndNewPaths, String guestOSType, VirtualMachine.State vmState) { + super(vmName, snapshot, null, guestOSType); this.vmState = vmState; + this.volumeTosAndNewPaths = volumeTosAndNewPaths; } public VirtualMachine.State getVmState() { return vmState; } + + public List> getVolumeTosAndNewPaths() { + return volumeTosAndNewPaths; + } } diff --git a/core/src/main/java/com/cloud/agent/api/storage/DownloadAnswer.java b/core/src/main/java/com/cloud/agent/api/storage/DownloadAnswer.java index 0c6373134b18..1c5eb7b9a9af 100644 --- a/core/src/main/java/com/cloud/agent/api/storage/DownloadAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/storage/DownloadAnswer.java @@ -140,7 +140,7 @@ public void setTemplateSize(long templateSize) { } public Long getTemplateSize() { - return templateSize; + return templateSize == 0 ? templatePhySicalSize : templateSize; } public void setTemplatePhySicalSize(long templatePhySicalSize) { 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 index b6396c24d10a..1a47d97d5e25 100644 --- a/core/src/main/java/com/cloud/agent/api/storage/MergeDiskOnlyVmSnapshotCommand.java +++ b/core/src/main/java/com/cloud/agent/api/storage/MergeDiskOnlyVmSnapshotCommand.java @@ -19,28 +19,28 @@ package com.cloud.agent.api.storage; import com.cloud.agent.api.Command; -import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.storage.to.DeltaMergeTreeTO; import java.util.List; public class MergeDiskOnlyVmSnapshotCommand extends Command { - private List snapshotMergeTreeToList; - private VirtualMachine.State vmState; + private List snapshotMergeTreeToList; + private boolean isVmRunning; private String vmName; - public MergeDiskOnlyVmSnapshotCommand(List snapshotMergeTreeToList, VirtualMachine.State vmState, String vmName) { + public MergeDiskOnlyVmSnapshotCommand(List snapshotMergeTreeToList, boolean isVmRunning, String vmName) { this.snapshotMergeTreeToList = snapshotMergeTreeToList; - this.vmState = vmState; + this.isVmRunning = isVmRunning; this.vmName = vmName; } - public List getSnapshotMergeTreeToList() { + public List getDeltaMergeTreeToList() { return snapshotMergeTreeToList; } - public VirtualMachine.State getVmState() { - return vmState; + public boolean isVmRunning() { + return isVmRunning; } public String getVmName() { 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 bd632632ae89..b9ac455130f3 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 @@ -267,13 +267,13 @@ private Answer execute(UpdateNetworkCommand cmd) { if (result.getDetails().contains(String.format("Interface with IP %s not found", ipAddressTO.getPublicIp()))) { logger.warn(String.format("Skipping IP: %s as it isn't configured on router interface", ipAddressTO.getPublicIp())); } else if (ipAddressTO.getDetails().get(ApiConstants.REDUNDANT_STATE).equals(VirtualRouter.RedundantState.PRIMARY.name())) { - logger.warn(String.format("Failed to update interface mtu to %s on interface with ip: %s", + logger.warn(String.format("Failed to update interface MTU to %s on interface with IP: %s", ipAddressTO.getMtu(), ipAddressTO.getPublicIp())); finalResult = false; } continue; } - logger.info(String.format("Successfully updated mtu to %s on interface with ip: %s", + logger.info(String.format("Successfully updated MTU to %s on interface with IP: %s", ipAddressTO.getMtu(), ipAddressTO.getPublicIp())); finalResult &= true; } catch (Exception e) { diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/DhcpEntryConfigItem.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/DhcpEntryConfigItem.java index 9c5b657bb4e4..041645046b23 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/DhcpEntryConfigItem.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/DhcpEntryConfigItem.java @@ -35,7 +35,7 @@ public List generateConfig(final NetworkElementCommand cmd) { final DhcpEntryCommand command = (DhcpEntryCommand) cmd; final VmDhcpConfig vmDhcpConfig = new VmDhcpConfig(command.getVmName(), command.getVmMac(), command.getVmIpAddress(), command.getVmIp6Address(), command.getDuid(), command.getDefaultDns(), - command.getDefaultRouter(), command.getStaticRoutes(), command.isDefault(), command.isRemove()); + command.getDefaultRouter(), command.getStaticRoutes(), command.isDefault(), command.isRemove(), command.getLeaseTime()); return generateConfigItems(vmDhcpConfig); } diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/VmDhcpConfig.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/VmDhcpConfig.java index d9cb8b0b2645..1c43cd1823b9 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/VmDhcpConfig.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/VmDhcpConfig.java @@ -29,6 +29,7 @@ public class VmDhcpConfig extends ConfigBase { private String defaultGateway; private String staticRoutes; private boolean defaultEntry; + private Long leaseTime; // Indicate if the entry should be removed when set to true private boolean remove; @@ -39,6 +40,11 @@ public VmDhcpConfig() { public VmDhcpConfig(String hostName, String macAddress, String ipv4Address, String ipv6Address, String ipv6Duid, String dnsAddresses, String defaultGateway, String staticRoutes, boolean defaultEntry, boolean remove) { + this(hostName, macAddress, ipv4Address, ipv6Address, ipv6Duid, dnsAddresses, defaultGateway, staticRoutes, defaultEntry, remove, null); + } + + public VmDhcpConfig(String hostName, String macAddress, String ipv4Address, String ipv6Address, String ipv6Duid, String dnsAddresses, String defaultGateway, + String staticRoutes, boolean defaultEntry, boolean remove, Long leaseTime) { super(VM_DHCP); this.hostName = hostName; this.macAddress = macAddress; @@ -50,6 +56,7 @@ public VmDhcpConfig(String hostName, String macAddress, String ipv4Address, Stri this.staticRoutes = staticRoutes; this.defaultEntry = defaultEntry; this.remove = remove; + this.leaseTime = leaseTime; } public String getHostName() { @@ -132,4 +139,12 @@ public void setDefaultEntry(boolean defaultEntry) { this.defaultEntry = defaultEntry; } + public Long getLeaseTime() { + return leaseTime; + } + + public void setLeaseTime(Long leaseTime) { + this.leaseTime = leaseTime; + } + } diff --git a/core/src/main/java/com/cloud/network/HAProxyConfigurator.java b/core/src/main/java/com/cloud/network/HAProxyConfigurator.java index 7736bea3cdaf..7d544c2e49c0 100644 --- a/core/src/main/java/com/cloud/network/HAProxyConfigurator.java +++ b/core/src/main/java/com/cloud/network/HAProxyConfigurator.java @@ -629,15 +629,25 @@ public String[] generateConfiguration(final LoadBalancerConfigCommand lbCmd) { } } result.addAll(gSection); - // TODO decide under what circumstances these options are needed - // result.add("\tnokqueue"); - // result.add("\tnopoll"); result.add(blankLine); final List dSection = Arrays.asList(defaultsSection); if (lbCmd.keepAliveEnabled) { dSection.set(7, "\tno option httpclose"); } + if (lbCmd.idleTimeout > 0) { + dSection.set(9, "\ttimeout client " + Long.toString(lbCmd.idleTimeout)); + dSection.set(10, "\ttimeout server " + Long.toString(lbCmd.idleTimeout)); + } else if (lbCmd.idleTimeout == 0) { + // .remove() is not allowed, only .set() operations are allowed as the list + // is a fixed size. So lets just mark the entry as blank. + dSection.set(9, ""); + dSection.set(10, ""); + } else { + // Negative idleTimeout values are considered invalid; retain the + // default HAProxy timeout values from defaultsSection for predictability. + logger.warn("Negative idleTimeout ({}) configured; retaining default HAProxy timeouts.", lbCmd.idleTimeout); + } if (logger.isDebugEnabled()) { for (final String s : dSection) { diff --git a/core/src/main/java/com/cloud/storage/resource/StorageProcessor.java b/core/src/main/java/com/cloud/storage/resource/StorageProcessor.java index b7cd5b8eb5ee..31c384eab3a0 100644 --- a/core/src/main/java/com/cloud/storage/resource/StorageProcessor.java +++ b/core/src/main/java/com/cloud/storage/resource/StorageProcessor.java @@ -21,7 +21,7 @@ import org.apache.cloudstack.agent.directdownload.DirectDownloadCommand; import org.apache.cloudstack.storage.command.AttachCommand; -import org.apache.cloudstack.storage.command.CheckDataStoreStoragePolicyComplainceCommand; +import org.apache.cloudstack.storage.command.CheckDataStoreStoragePolicyComplianceCommand; import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.cloudstack.storage.command.CreateObjectCommand; import org.apache.cloudstack.storage.command.DeleteCommand; @@ -82,7 +82,11 @@ public interface StorageProcessor { Answer copyVolumeFromPrimaryToPrimary(CopyCommand cmd); - public Answer checkDataStoreStoragePolicyCompliance(CheckDataStoreStoragePolicyComplainceCommand cmd); + public Answer checkDataStoreStoragePolicyCompliance(CheckDataStoreStoragePolicyComplianceCommand cmd); public Answer syncVolumePath(SyncVolumePathCommand cmd); + + default Answer deleteBackup(DeleteCommand cmd) { + return new Answer(cmd, false, "Operation not implemented"); + } } 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 588852101206..3d2608c0c4b8 100644 --- a/core/src/main/java/com/cloud/storage/resource/StorageSubsystemCommandHandlerBase.java +++ b/core/src/main/java/com/cloud/storage/resource/StorageSubsystemCommandHandlerBase.java @@ -21,7 +21,7 @@ import org.apache.cloudstack.agent.directdownload.DirectDownloadCommand; import org.apache.cloudstack.storage.command.AttachCommand; -import org.apache.cloudstack.storage.command.CheckDataStoreStoragePolicyComplainceCommand; +import org.apache.cloudstack.storage.command.CheckDataStoreStoragePolicyComplianceCommand; import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.cloudstack.storage.command.CreateObjectAnswer; import org.apache.cloudstack.storage.command.CreateObjectCommand; @@ -79,8 +79,8 @@ public Answer handleStorageCommands(StorageSubSystemCommand command) { return processor.resignature((ResignatureCommand) command); } else if (command instanceof DirectDownloadCommand) { return processor.handleDownloadTemplateToPrimaryStorage((DirectDownloadCommand) command); - } else if (command instanceof CheckDataStoreStoragePolicyComplainceCommand) { - return processor.checkDataStoreStoragePolicyCompliance((CheckDataStoreStoragePolicyComplainceCommand) command); + } else if (command instanceof CheckDataStoreStoragePolicyComplianceCommand) { + return processor.checkDataStoreStoragePolicyCompliance((CheckDataStoreStoragePolicyComplianceCommand) command); } else if (command instanceof SyncVolumePathCommand) { return processor.syncVolumePath((SyncVolumePathCommand) command); } else if (command instanceof QuerySnapshotZoneCopyCommand) { @@ -154,6 +154,8 @@ protected Answer execute(DeleteCommand cmd) { answer = processor.deleteVolume(cmd); } else if (data.getObjectType() == DataObjectType.SNAPSHOT) { answer = processor.deleteSnapshot(cmd); + } else if (data.getObjectType() == DataObjectType.BACKUP) { + answer = processor.deleteBackup(cmd); } else { answer = new Answer(cmd, false, "unsupported type"); } 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 4c056f256cf0..71c329796d1b 100755 --- a/core/src/main/java/com/cloud/storage/template/HttpTemplateDownloader.java +++ b/core/src/main/java/com/cloud/storage/template/HttpTemplateDownloader.java @@ -52,6 +52,7 @@ import com.cloud.utils.Pair; import com.cloud.utils.UriUtils; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.net.HttpClientCloudStackUserAgent; import com.cloud.utils.net.Proxy; /** @@ -125,6 +126,7 @@ private GetMethod createRequest(String downloadUrl) { GetMethod request = new GetMethod(downloadUrl); request.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, myretryhandler); request.setFollowRedirects(followRedirects); + request.getParams().setParameter(HttpMethodParams.USER_AGENT, HttpClientCloudStackUserAgent.CLOUDSTACK_USER_AGENT); return request; } @@ -151,7 +153,7 @@ private void checkCredentials(String user, String password) { client.getParams().setAuthenticationPreemptive(true); Credentials defaultcreds = new UsernamePasswordCredentials(user, password); client.getState().setCredentials(new AuthScope(hostAndPort.first(), hostAndPort.second(), AuthScope.ANY_REALM), defaultcreds); - logger.info("Added username=" + user + ", password=" + password + "for host " + hostAndPort.first() + ":" + hostAndPort.second()); + logger.info("Added username={}, password=****** for host {}:{}", user, hostAndPort.first(), hostAndPort.second()); } else { logger.info("No credentials configured for host=" + hostAndPort.first() + ":" + hostAndPort.second()); } @@ -578,7 +580,7 @@ public VerifyFormat invoke() { logger.debug("Error on http connection : " + ex.getMessage()); } status = Status.UNRECOVERABLE_ERROR; - errorString = "Template content is unsupported, or mismatch between selected format and template content. Found : " + unsupportedFormat; + errorString = "Template content is unsupported, or mismatch between selected format and Template content. Found : " + unsupportedFormat; throw new CloudRuntimeException(errorString); } else { logger.debug("Verified format of downloading file " + file.getAbsolutePath() + " is supported"); diff --git a/core/src/main/java/com/cloud/storage/template/IsoProcessor.java b/core/src/main/java/com/cloud/storage/template/IsoProcessor.java index 6ab42effb524..76d19d53453b 100644 --- a/core/src/main/java/com/cloud/storage/template/IsoProcessor.java +++ b/core/src/main/java/com/cloud/storage/template/IsoProcessor.java @@ -48,7 +48,7 @@ public FormatInfo process(String templatePath, ImageFormat format, String templa String isoPath = templatePath + File.separator + templateName + "." + ImageFormat.ISO.getFileExtension(); if (!_storage.exists(isoPath)) { - logger.debug("Unable to find the iso file: " + isoPath); + logger.debug("Unable to find the ISO file: " + isoPath); return null; } diff --git a/core/src/main/java/com/cloud/storage/template/MetalinkTemplateDownloader.java b/core/src/main/java/com/cloud/storage/template/MetalinkTemplateDownloader.java index 2e62809dfeda..0dad2564779c 100644 --- a/core/src/main/java/com/cloud/storage/template/MetalinkTemplateDownloader.java +++ b/core/src/main/java/com/cloud/storage/template/MetalinkTemplateDownloader.java @@ -18,8 +18,11 @@ // package com.cloud.storage.template; + import com.cloud.storage.StorageLayer; import com.cloud.utils.UriUtils; +import com.cloud.utils.net.HttpClientCloudStackUserAgent; + import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpMethodRetryHandler; @@ -59,6 +62,7 @@ protected GetMethod createRequest(String downloadUrl) { GetMethod request = new GetMethod(downloadUrl); request.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, myretryhandler); request.setFollowRedirects(followRedirects); + request.getParams().setParameter(HttpMethodParams.USER_AGENT, HttpClientCloudStackUserAgent.CLOUDSTACK_USER_AGENT); if (!toFileSet) { String[] parts = downloadUrl.split("/"); String filename = parts[parts.length - 1]; @@ -108,7 +112,7 @@ private boolean performDownload() { ) { IOUtils.copy(in, out); } catch (IOException e) { - logger.error("Error downloading template from: " + _downloadUrl + " due to: " + e.getMessage()); + logger.error("Error downloading Template from: " + _downloadUrl + " due to: " + e.getMessage()); return false; } return true; diff --git a/core/src/main/java/com/cloud/storage/template/OVAProcessor.java b/core/src/main/java/com/cloud/storage/template/OVAProcessor.java index ab3aa0d0e3a5..28ab3b58d45d 100644 --- a/core/src/main/java/com/cloud/storage/template/OVAProcessor.java +++ b/core/src/main/java/com/cloud/storage/template/OVAProcessor.java @@ -68,7 +68,7 @@ public FormatInfo process(String templatePath, ImageFormat format, String templa String templateFilePath = templatePath + File.separator + templateName + "." + ImageFormat.OVA.getFileExtension(); if (!_storage.exists(templateFilePath)) { if (logger.isInfoEnabled()) { - logger.info("Unable to find the vmware template file: " + templateFilePath); + logger.info("Unable to find the VMware Template file: " + templateFilePath); } return null; } @@ -113,7 +113,7 @@ private OVFInformationTO createOvfInformationTO(OVFHelper ovfHelper, Document do List disks = ovfHelper.getOVFVolumeInfoFromFile(ovfFilePath, doc, null); if (CollectionUtils.isNotEmpty(disks)) { if (logger.isTraceEnabled()) { - logger.trace(String.format("Found %d disks in template %s", disks.size(), ovfFilePath)); + logger.trace(String.format("Found %d disks in Template %s", disks.size(), ovfFilePath)); } ovfInformationTO.setDisks(disks); } @@ -122,14 +122,14 @@ private OVFInformationTO createOvfInformationTO(OVFHelper ovfHelper, Document do logger.info("Found " + nets.size() + " prerequisite networks"); ovfInformationTO.setNetworks(nets); } else if (logger.isTraceEnabled()) { - logger.trace(String.format("no net prerequisites found in template %s", ovfFilePath)); + logger.trace(String.format("No net prerequisites found in Template %s", ovfFilePath)); } List ovfProperties = ovfHelper.getConfigurableOVFPropertiesFromDocument(doc); if (CollectionUtils.isNotEmpty(ovfProperties)) { logger.info("Found " + ovfProperties.size() + " configurable OVF properties"); ovfInformationTO.setProperties(ovfProperties); } else if (logger.isTraceEnabled()) { - logger.trace(String.format("no ovf properties found in template %s", ovfFilePath)); + logger.trace(String.format("No OVF properties found in Template %s", ovfFilePath)); } OVFVirtualHardwareSectionTO hardwareSection = ovfHelper.getVirtualHardwareSectionFromDocument(doc); List configurations = hardwareSection.getConfigurations(); @@ -213,7 +213,7 @@ public long getVirtualSize(File file) { return size; } catch (Exception e) { logger.info("[ignored]" - + "failed to get virtual template size for ova: " + e.getLocalizedMessage()); + + "failed to get virtual Template size for ova: " + e.getLocalizedMessage()); } return file.length(); } @@ -231,7 +231,7 @@ public long getTemplateVirtualSize(String templatePath, String templateName) thr String ovfFileName = getOVFFilePath(templateFileFullPath); OVFHelper ovfHelper = new OVFHelper(); if (ovfFileName == null) { - String msg = "Unable to locate OVF file in template package directory: " + templatePath; + String msg = "Unable to locate OVF file in Template package directory: " + templatePath; logger.error(msg); throw new InternalErrorException(msg); } diff --git a/core/src/main/java/com/cloud/storage/template/QCOW2Processor.java b/core/src/main/java/com/cloud/storage/template/QCOW2Processor.java index df1722a0201d..547c990fbafa 100644 --- a/core/src/main/java/com/cloud/storage/template/QCOW2Processor.java +++ b/core/src/main/java/com/cloud/storage/template/QCOW2Processor.java @@ -81,7 +81,7 @@ public long getVirtualSize(File file) throws IOException { long size = getTemplateVirtualSize(file); return size; } catch (Exception e) { - logger.info("[ignored]" + "failed to get template virtual size for QCOW2: " + e.getLocalizedMessage()); + logger.info("[ignored]" + "failed to get Template virtual size for QCOW2: " + e.getLocalizedMessage()); } return file.length(); } diff --git a/core/src/main/java/com/cloud/storage/template/S3TemplateDownloader.java b/core/src/main/java/com/cloud/storage/template/S3TemplateDownloader.java index c24a4cc221fc..70df906d1ce3 100644 --- a/core/src/main/java/com/cloud/storage/template/S3TemplateDownloader.java +++ b/core/src/main/java/com/cloud/storage/template/S3TemplateDownloader.java @@ -150,7 +150,7 @@ public long download(boolean resume, DownloadCompleteCallback callback) { } if (remoteSize > maxTemplateSizeInByte) { - errorString = "Remote size is too large for template " + downloadUrl + " remote size is " + remoteSize + " max allowed is " + maxTemplateSizeInByte; + errorString = "Remote size is too large for Template " + downloadUrl + " remote size is " + remoteSize + " max allowed is " + maxTemplateSizeInByte; logger.warn(errorString); status = Status.UNRECOVERABLE_ERROR; @@ -162,7 +162,7 @@ public long download(boolean resume, DownloadCompleteCallback callback) { try { inputStream = new BufferedInputStream(getMethod.getResponseBodyAsStream()); } catch (IOException e) { - errorString = "Exception occurred while opening InputStream for template " + downloadUrl; + errorString = "Exception occurred while opening InputStream for Template " + downloadUrl; logger.warn(errorString); status = Status.UNRECOVERABLE_ERROR; diff --git a/core/src/main/java/com/cloud/storage/template/SimpleHttpMultiFileDownloader.java b/core/src/main/java/com/cloud/storage/template/SimpleHttpMultiFileDownloader.java index 8719947cb4f0..6608754073a1 100644 --- a/core/src/main/java/com/cloud/storage/template/SimpleHttpMultiFileDownloader.java +++ b/core/src/main/java/com/cloud/storage/template/SimpleHttpMultiFileDownloader.java @@ -44,6 +44,7 @@ import org.apache.commons.lang3.StringUtils; import com.cloud.storage.StorageLayer; +import com.cloud.utils.net.HttpClientCloudStackUserAgent; public class SimpleHttpMultiFileDownloader extends ManagedContextRunnable implements TemplateDownloader { private static final MultiThreadedHttpConnectionManager s_httpClientManager = new MultiThreadedHttpConnectionManager(); @@ -95,6 +96,7 @@ private GetMethod createRequest(String downloadUrl) { GetMethod request = new GetMethod(downloadUrl); request.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, retryHandler); request.setFollowRedirects(followRedirects); + request.getParams().setParameter(HttpMethodParams.USER_AGENT, HttpClientCloudStackUserAgent.CLOUDSTACK_USER_AGENT); return request; } @@ -141,6 +143,7 @@ private void tryAndGetTotalRemoteSize() { continue; } HeadMethod headMethod = new HeadMethod(downloadUrl); + headMethod.getParams().setParameter(HttpMethodParams.USER_AGENT, HttpClientCloudStackUserAgent.CLOUDSTACK_USER_AGENT); try { if (client.executeMethod(headMethod) != HttpStatus.SC_OK) { continue; diff --git a/core/src/main/java/com/cloud/storage/template/TemplateLocation.java b/core/src/main/java/com/cloud/storage/template/TemplateLocation.java index 563c642f292e..4cd4d32d255c 100644 --- a/core/src/main/java/com/cloud/storage/template/TemplateLocation.java +++ b/core/src/main/java/com/cloud/storage/template/TemplateLocation.java @@ -103,7 +103,7 @@ public boolean load() throws IOException { try (FileInputStream strm = new FileInputStream(_file);) { _props.load(strm); } catch (IOException e) { - logger.warn("Unable to load the template properties for '" + _file + "': ", e); + logger.warn("Unable to load the Template properties for '" + _file + "': ", e); } for (ImageFormat format : ImageFormat.values()) { @@ -161,7 +161,7 @@ public boolean save() { try (FileOutputStream strm = new FileOutputStream(_file);) { _props.store(strm, ""); } catch (IOException e) { - logger.warn("Unable to save the template properties ", e); + logger.warn("Unable to save the Template properties ", e); return false; } return true; diff --git a/core/src/main/java/com/cloud/storage/template/VhdProcessor.java b/core/src/main/java/com/cloud/storage/template/VhdProcessor.java index 9f18d782b426..e9be8054506d 100644 --- a/core/src/main/java/com/cloud/storage/template/VhdProcessor.java +++ b/core/src/main/java/com/cloud/storage/template/VhdProcessor.java @@ -95,7 +95,7 @@ public long getVirtualSize(File file) throws IOException { long size = getTemplateVirtualSize(file); return size; } catch (Exception e) { - logger.info("[ignored]" + "failed to get template virtual size for VHD: " + e.getLocalizedMessage()); + logger.info("[ignored]" + "failed to get Template virtual size for VHD: " + e.getLocalizedMessage()); } return file.length(); } diff --git a/core/src/main/java/com/cloud/storage/template/VmdkProcessor.java b/core/src/main/java/com/cloud/storage/template/VmdkProcessor.java index 4f53c556667f..292c1f4f52e4 100644 --- a/core/src/main/java/com/cloud/storage/template/VmdkProcessor.java +++ b/core/src/main/java/com/cloud/storage/template/VmdkProcessor.java @@ -60,7 +60,7 @@ public FormatInfo process(String templatePath, ImageFormat format, String templa String templateFilePath = templatePath + File.separator + templateName + "." + ImageFormat.VMDK.getFileExtension(); if (!_storage.exists(templateFilePath)) { if (logger.isInfoEnabled()) { - logger.info("Unable to find the vmware template file: " + templateFilePath); + logger.info("Unable to find the VMware Template file: " + templateFilePath); } return null; } @@ -81,7 +81,7 @@ public long getVirtualSize(File file) { return size; } catch (Exception e) { logger.info("[ignored]" - + "failed to get template virtual size for vmdk: " + e.getLocalizedMessage()); + + "failed to get Template virtual size for vmdk: " + e.getLocalizedMessage()); } return file.length(); } 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 ffc67b628a7e..abe78ee5553d 100644 --- a/core/src/main/java/org/apache/cloudstack/backup/BackupAnswer.java +++ b/core/src/main/java/org/apache/cloudstack/backup/BackupAnswer.java @@ -29,6 +29,12 @@ public class BackupAnswer extends Answer { private Long virtualSize; private Map volumes; Boolean needsCleanup; + // Set by the NAS backup provider after a checkpoint/bitmap was created during this backup. + // The provider persists it in backup_details under NASBackupChainKeys.BITMAP_NAME. + private String bitmapCreated; + // Set when an incremental was requested but the agent had to fall back to a full + // (e.g. VM was stopped). Provider should record this backup as type=full. + private Boolean incrementalFallback; public BackupAnswer(final Command command, final boolean success, final String details) { super(command, success, details); @@ -68,4 +74,21 @@ public Boolean getNeedsCleanup() { public void setNeedsCleanup(Boolean needsCleanup) { this.needsCleanup = needsCleanup; } + + public String getBitmapCreated() { + return bitmapCreated; + } + + public void setBitmapCreated(String bitmapCreated) { + this.bitmapCreated = bitmapCreated; + } + + public Boolean getIncrementalFallback() { + return incrementalFallback != null && incrementalFallback; + } + + public void setIncrementalFallback(Boolean incrementalFallback) { + this.incrementalFallback = incrementalFallback; + } + } diff --git a/core/src/main/java/org/apache/cloudstack/backup/CleanupKbossBackupErrorAnswer.java b/core/src/main/java/org/apache/cloudstack/backup/CleanupKbossBackupErrorAnswer.java new file mode 100644 index 000000000000..34b65adc2467 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/CleanupKbossBackupErrorAnswer.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.backup; + +import java.util.List; + +import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.commons.collections4.CollectionUtils; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; + +public class CleanupKbossBackupErrorAnswer extends Answer { + private List volumeObjectTos; + private boolean vmRunning; + + public CleanupKbossBackupErrorAnswer(Command cmd, List volumeObjectTos, boolean vmRunning) { + super(cmd, CollectionUtils.isNotEmpty(volumeObjectTos), null); + this.volumeObjectTos = volumeObjectTos; + this.vmRunning = vmRunning; + } + + public List getVolumeObjectTos() { + return volumeObjectTos; + } + + public boolean isVmRunning() { + return vmRunning; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/CleanupKbossBackupErrorCommand.java b/core/src/main/java/org/apache/cloudstack/backup/CleanupKbossBackupErrorCommand.java new file mode 100644 index 000000000000..3257851d11ba --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/CleanupKbossBackupErrorCommand.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.backup; + +import com.cloud.agent.api.Command; +import org.apache.cloudstack.storage.to.KbossTO; + +import java.util.List; + +public class CleanupKbossBackupErrorCommand extends Command { + + private boolean runningVM; + + private String vmName; + + private String imageStoreUrl; + + private List kbossTOS; + + public CleanupKbossBackupErrorCommand(boolean runningVM, String vmName, String imageStoreUrl, List kbossTOS) { + this.runningVM = runningVM; + this.vmName = vmName; + this.imageStoreUrl = imageStoreUrl; + this.kbossTOS = kbossTOS; + } + + public boolean isRunningVM() { + return runningVM; + } + + public String getVmName() { + return vmName; + } + + public String getImageStoreUrl() { + return imageStoreUrl; + } + + public List getKbossTOs() { + return kbossTOS; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/CleanupKbossValidationCommand.java b/core/src/main/java/org/apache/cloudstack/backup/CleanupKbossValidationCommand.java new file mode 100644 index 000000000000..8a345aeba176 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/CleanupKbossValidationCommand.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 org.apache.cloudstack.backup; + +import com.cloud.agent.api.Command; + +import java.util.Set; + +public class CleanupKbossValidationCommand extends Command { + + private String vmName; + + private Set secondaryStorages; + + public CleanupKbossValidationCommand(String vmName, Set secondaryStorages) { + this.vmName = vmName; + this.secondaryStorages = secondaryStorages; + } + + public String getVmName() { + return vmName; + } + + public Set getSecondaryStorages() { + return secondaryStorages; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/CompressBackupCommand.java b/core/src/main/java/org/apache/cloudstack/backup/CompressBackupCommand.java new file mode 100644 index 000000000000..a551f246a0bb --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/CompressBackupCommand.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.backup; + +import com.cloud.agent.api.Command; +import org.apache.cloudstack.storage.to.DeltaMergeTreeTO; + +import java.util.List; + +public class CompressBackupCommand extends Command { + + private List backupDeltasToCompress; + + private List backupChainImageStoreUrls; + + private long minFreeStorage; + + private Backup.CompressionLibrary compressionLib; + + private int coroutines; + + private int rateLimit; + + public CompressBackupCommand(List backupDeltasToCompress, List backupChainImageStoreUrls, long minFreeStorage, Backup.CompressionLibrary compressionLib, int coroutines, int rateLimit) { + this.backupChainImageStoreUrls = backupChainImageStoreUrls; + this.backupDeltasToCompress = backupDeltasToCompress; + this.minFreeStorage = minFreeStorage; + this.compressionLib = compressionLib; + this.coroutines = coroutines; + this.rateLimit = rateLimit; + } + + public List getBackupDeltasToCompress() { + return backupDeltasToCompress; + } + + public List getBackupChainImageStoreUrls() { + return backupChainImageStoreUrls; + } + + public long getMinFreeStorage() { + return minFreeStorage; + } + + public Backup.CompressionLibrary getCompressionLib() { + return compressionLib; + } + + public int getCoroutines() { + return coroutines; + } + + public int getRateLimit() { + return rateLimit; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/ConsolidateVolumesAnswer.java b/core/src/main/java/org/apache/cloudstack/backup/ConsolidateVolumesAnswer.java new file mode 100644 index 000000000000..9f230deb8d75 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/ConsolidateVolumesAnswer.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.backup; + +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 ConsolidateVolumesAnswer extends Answer { + + private List successfullyConsolidatedVolumes; + + public ConsolidateVolumesAnswer(Command command, boolean success, String details, List successfullyConsolidatedVolumes) { + super(command, success, details); + this.successfullyConsolidatedVolumes = successfullyConsolidatedVolumes; + } + + public List getSuccessfullyConsolidatedVolumes() { + return successfullyConsolidatedVolumes; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/ConsolidateVolumesCommand.java b/core/src/main/java/org/apache/cloudstack/backup/ConsolidateVolumesCommand.java new file mode 100644 index 000000000000..7b2bc2245939 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/ConsolidateVolumesCommand.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.backup; + +import com.cloud.agent.api.Command; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; +import org.apache.cloudstack.storage.to.VolumeObjectTO; + +import java.util.List; +import java.util.stream.Collectors; + +public class ConsolidateVolumesCommand extends Command { + + private List volumesToConsolidate; + + private List secondaryStorageUuids; + + private String vmName; + + public ConsolidateVolumesCommand(List volumesToConsolidate, List secondaryStorageUuids, String vmName) { + this.volumesToConsolidate = volumesToConsolidate.stream().map(vol -> (VolumeObjectTO)vol.getTO()).collect(Collectors.toList()); + this.secondaryStorageUuids = secondaryStorageUuids; + this.vmName = vmName; + } + + public List getVolumesToConsolidate() { + return volumesToConsolidate; + } + + public List getSecondaryStorageUuids() { + return secondaryStorageUuids; + } + + public String getVmName() { + return vmName; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/CreateImageTransferAnswer.java b/core/src/main/java/org/apache/cloudstack/backup/CreateImageTransferAnswer.java new file mode 100644 index 000000000000..34cf6d4ca34c --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/CreateImageTransferAnswer.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 +//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; + +public class CreateImageTransferAnswer extends Answer { + private String imageTransferId; + private String transferUrl; + + public CreateImageTransferAnswer() { + } + + public CreateImageTransferAnswer(CreateImageTransferCommand cmd, boolean success, String details) { + super(cmd, success, details); + } + + public CreateImageTransferAnswer(CreateImageTransferCommand cmd, boolean success, String details, + String imageTransferId, String transferUrl) { + super(cmd, success, details); + this.imageTransferId = imageTransferId; + this.transferUrl = transferUrl; + } + + public String getImageTransferId() { + return imageTransferId; + } + + public void setImageTransferId(String imageTransferId) { + this.imageTransferId = imageTransferId; + } + + public String getTransferUrl() { + return transferUrl; + } + + public void setTransferUrl(String transferUrl) { + this.transferUrl = transferUrl; + } + +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/CreateImageTransferCommand.java b/core/src/main/java/org/apache/cloudstack/backup/CreateImageTransferCommand.java new file mode 100644 index 000000000000..95b56c9a9c38 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/CreateImageTransferCommand.java @@ -0,0 +1,94 @@ +//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.backup; + +import com.cloud.agent.api.Command; + +public class CreateImageTransferCommand extends Command { + private String transferId; + private String exportName; + private String socket; + private String direction; + private String checkpointId; + private String file; + private ImageTransfer.Backend backend; + private int idleTimeoutSeconds; + + public CreateImageTransferCommand() { + } + + private CreateImageTransferCommand(String transferId, String direction, String socket, int idleTimeoutSeconds) { + this.transferId = transferId; + this.direction = direction; + this.socket = socket; + this.idleTimeoutSeconds = idleTimeoutSeconds; + } + + public CreateImageTransferCommand(String transferId, String direction, String exportName, String socket, String checkpointId, int idleTimeoutSeconds) { + this(transferId, direction, socket, idleTimeoutSeconds); + this.backend = ImageTransfer.Backend.nbd; + this.exportName = exportName; + this.checkpointId = checkpointId; + } + + public CreateImageTransferCommand(String transferId, String direction, String socket, String file, int idleTimeoutSeconds) { + this(transferId, direction, socket, idleTimeoutSeconds); + if (direction == ImageTransfer.Direction.download.toString()) { + throw new IllegalArgumentException("File backend is only supported for upload"); + } + this.backend = ImageTransfer.Backend.file; + this.file = file; + } + + public String getExportName() { + return exportName; + } + + public String getSocket() { + return socket; + } + + public String getFile() { + return file; + } + + public ImageTransfer.Backend getBackend() { + return backend; + } + + public String getTransferId() { + return transferId; + } + + @Override + public boolean executeInSequence() { + return true; + } + + public String getDirection() { + return direction; + } + + public String getCheckpointId() { + return checkpointId; + } + + public int getIdleTimeoutSeconds() { + return idleTimeoutSeconds; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/DeleteVmCheckpointCommand.java b/core/src/main/java/org/apache/cloudstack/backup/DeleteVmCheckpointCommand.java new file mode 100644 index 000000000000..81cf6c1abfcc --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/DeleteVmCheckpointCommand.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 +//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 java.util.Map; + +import com.cloud.agent.api.Command; + +public class DeleteVmCheckpointCommand extends Command { + private String vmName; + private String checkpointId; + private Map diskPathUuidMap; + private boolean stoppedVM; + + public DeleteVmCheckpointCommand() { + } + + public DeleteVmCheckpointCommand(String vmName, String checkpointId, Map diskPathUuidMap, boolean stoppedVM) { + this.vmName = vmName; + this.checkpointId = checkpointId; + this.diskPathUuidMap = diskPathUuidMap; + this.stoppedVM = stoppedVM; + } + + public String getVmName() { + return vmName; + } + + public String getCheckpointId() { + return checkpointId; + } + + public Map getDiskPathUuidMap() { + return diskPathUuidMap; + } + + public boolean isStoppedVM() { + return stoppedVM; + } + + @Override + public boolean executeInSequence() { + return true; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/FinalizeBackupCompressionCommand.java b/core/src/main/java/org/apache/cloudstack/backup/FinalizeBackupCompressionCommand.java new file mode 100644 index 000000000000..a4cbb69611f7 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/FinalizeBackupCompressionCommand.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 org.apache.cloudstack.backup; + +import com.cloud.agent.api.Command; +import org.apache.cloudstack.storage.to.BackupDeltaTO; + +import java.util.List; + +public class FinalizeBackupCompressionCommand extends Command { + private boolean cleanup; + + private List backupDeltaTO; + + public FinalizeBackupCompressionCommand(boolean cleanup, List backupDeltaTO) { + this.cleanup = cleanup; + this.backupDeltaTO = backupDeltaTO; + } + + public boolean isCleanup() { + return cleanup; + } + + public List getBackupDeltaTOList() { + return backupDeltaTO; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/FinalizeImageTransferCommand.java b/core/src/main/java/org/apache/cloudstack/backup/FinalizeImageTransferCommand.java new file mode 100644 index 000000000000..84d9b1ff8186 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/FinalizeImageTransferCommand.java @@ -0,0 +1,40 @@ +//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.backup; + +import com.cloud.agent.api.Command; + +public class FinalizeImageTransferCommand extends Command { + private String transferId; + + public FinalizeImageTransferCommand() { + } + + public FinalizeImageTransferCommand(String transferId) { + this.transferId = transferId; + } + + public String getTransferId() { + return transferId; + } + + @Override + public boolean executeInSequence() { + return true; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/PrepareValidationCommand.java b/core/src/main/java/org/apache/cloudstack/backup/PrepareValidationCommand.java new file mode 100644 index 000000000000..bae2fee95774 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/PrepareValidationCommand.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 org.apache.cloudstack.backup; + +import com.cloud.agent.api.Command; +import com.cloud.utils.Pair; +import org.apache.cloudstack.storage.to.BackupDeltaTO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; + +import java.util.List; +import java.util.Set; + +public class PrepareValidationCommand extends Command { + + private List> backupToVolumeList; + + private Set imageStoreSet; + + public PrepareValidationCommand(List> backupToVolumeList, Set imageStoreSet) { + this.backupToVolumeList = backupToVolumeList; + this.imageStoreSet = imageStoreSet; + } + + public List> getBackupToVolumeList() { + return backupToVolumeList; + } + + public Set getImageStoreSet() { + return imageStoreSet; + } + + @Override + public boolean executeInSequence() { + return false; + } +} 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 f447fbe3d008..972c2eaf7bb4 100644 --- a/core/src/main/java/org/apache/cloudstack/backup/RestoreBackupCommand.java +++ b/core/src/main/java/org/apache/cloudstack/backup/RestoreBackupCommand.java @@ -22,6 +22,7 @@ import com.cloud.agent.api.Command; import com.cloud.agent.api.LogLevel; import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; import java.util.List; @@ -31,11 +32,14 @@ public class RestoreBackupCommand extends Command { private String backupRepoType; private String backupRepoAddress; private List backupVolumesUUIDs; + private List restoreVolumePools; private List restoreVolumePaths; + private List restoreVolumeSizes; + private List backupFiles; private String diskType; private Boolean vmExists; - private String restoreVolumeUUID; private VirtualMachine.State vmState; + private Integer mountTimeout; protected RestoreBackupCommand() { super(); @@ -73,6 +77,14 @@ public void setBackupRepoAddress(String backupRepoAddress) { this.backupRepoAddress = backupRepoAddress; } + public List getRestoreVolumePools() { + return restoreVolumePools; + } + + public void setRestoreVolumePools(List restoreVolumePools) { + this.restoreVolumePools = restoreVolumePools; + } + public List getRestoreVolumePaths() { return restoreVolumePaths; } @@ -81,6 +93,22 @@ public void setRestoreVolumePaths(List restoreVolumePaths) { this.restoreVolumePaths = restoreVolumePaths; } + public List getRestoreVolumeSizes() { + return restoreVolumeSizes; + } + + public void setRestoreVolumeSizes(List restoreVolumeSizes) { + this.restoreVolumeSizes = restoreVolumeSizes; + } + + public List getBackupFiles() { + return backupFiles; + } + + public void setBackupFiles(List backupFiles) { + this.backupFiles = backupFiles; + } + public Boolean isVmExists() { return vmExists; } @@ -105,14 +133,6 @@ public void setMountOptions(String mountOptions) { this.mountOptions = mountOptions; } - public String getRestoreVolumeUUID() { - return restoreVolumeUUID; - } - - public void setRestoreVolumeUUID(String restoreVolumeUUID) { - this.restoreVolumeUUID = restoreVolumeUUID; - } - public VirtualMachine.State getVmState() { return vmState; } @@ -136,4 +156,12 @@ public List getBackupVolumesUUIDs() { public void setBackupVolumesUUIDs(List backupVolumesUUIDs) { this.backupVolumesUUIDs = backupVolumesUUIDs; } + + public Integer getMountTimeout() { + return this.mountTimeout == null ? 0 : this.mountTimeout; + } + + public void setMountTimeout(Integer mountTimeout) { + this.mountTimeout = mountTimeout; + } } diff --git a/core/src/main/java/org/apache/cloudstack/backup/RestoreKbossBackupAnswer.java b/core/src/main/java/org/apache/cloudstack/backup/RestoreKbossBackupAnswer.java new file mode 100644 index 000000000000..925ceca6b3b3 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/RestoreKbossBackupAnswer.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 org.apache.cloudstack.backup; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; + +import java.util.Set; + +public class RestoreKbossBackupAnswer extends Answer { + + private Set secondaryStorageUuids; + + public RestoreKbossBackupAnswer(Command command, Set secondaryStorageUuids) { + super(command); + this.secondaryStorageUuids = secondaryStorageUuids; + } + + public RestoreKbossBackupAnswer(Command command, Exception e, Set secondaryStorageUuids) { + super(command, e); + this.secondaryStorageUuids = secondaryStorageUuids; + } + + public Set getSecondaryStorageUuids() { + return secondaryStorageUuids; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/RestoreKbossBackupCommand.java b/core/src/main/java/org/apache/cloudstack/backup/RestoreKbossBackupCommand.java new file mode 100644 index 000000000000..d173aab324a2 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/RestoreKbossBackupCommand.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.utils.Pair; +import org.apache.cloudstack.storage.to.BackupDeltaTO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; + +import java.util.Set; + +public class RestoreKbossBackupCommand extends Command { + + private Set deltasToRemove; + + private Set> backupAndVolumePairs; + + private Set secondaryStorageUrls; + + private boolean quickRestore; + + public RestoreKbossBackupCommand(Set deltasToRemove, Set> backupAndVolumePairs, Set secondaryStorageUrls, + boolean quickRestore) { + this.deltasToRemove = deltasToRemove; + this.backupAndVolumePairs = backupAndVolumePairs; + this.secondaryStorageUrls = secondaryStorageUrls; + this.quickRestore = quickRestore; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public Set getDeltasToRemove() { + return deltasToRemove; + } + + public Set> getBackupAndVolumePairs() { + return backupAndVolumePairs; + } + + public Set getSecondaryStorageUrls() { + return secondaryStorageUrls; + } + + public boolean isQuickRestore() { + return quickRestore; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/StartBackupAnswer.java b/core/src/main/java/org/apache/cloudstack/backup/StartBackupAnswer.java new file mode 100644 index 000000000000..d7cbf097df90 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/StartBackupAnswer.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 +//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; + +public class StartBackupAnswer extends Answer { + private Long checkpointCreateTime; + + public StartBackupAnswer() { + } + + public StartBackupAnswer(StartBackupCommand cmd, boolean success, String details) { + super(cmd, success, details); + } + + public StartBackupAnswer(StartBackupCommand cmd, boolean success, String details, Long checkpointCreateTime) { + super(cmd, success, details); + this.checkpointCreateTime = checkpointCreateTime; + } + + public Long getCheckpointCreateTime() { + return checkpointCreateTime; + } + + public void setCheckpointCreateTime(Long checkpointCreateTime) { + this.checkpointCreateTime = checkpointCreateTime; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/StartBackupCommand.java b/core/src/main/java/org/apache/cloudstack/backup/StartBackupCommand.java new file mode 100644 index 000000000000..6f1ed6834500 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/StartBackupCommand.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 +//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 java.util.Map; + +import com.cloud.agent.api.Command; +import com.cloud.agent.api.LogLevel; + +public class StartBackupCommand extends Command { + private String vmName; + private String toCheckpointId; + private String fromCheckpointId; + private Long fromCheckpointCreateTime; + private String socket; + private Map diskPathUuidMap; + private boolean stoppedVM; + @LogLevel(LogLevel.Log4jLevel.Off) + private Map diskPathPassphraseMap; + + public StartBackupCommand() { + } + + public StartBackupCommand(String vmName, String toCheckpointId, String fromCheckpointId, Long fromCheckpointCreateTime, + String socket, Map diskPathUuidMap, Map passphrases, boolean stoppedVM) { + this.vmName = vmName; + this.toCheckpointId = toCheckpointId; + this.fromCheckpointId = fromCheckpointId; + this.fromCheckpointCreateTime = fromCheckpointCreateTime; + this.socket = socket; + this.diskPathUuidMap = diskPathUuidMap; + this.diskPathPassphraseMap = passphrases; + this.stoppedVM = stoppedVM; + } + + public String getVmName() { + return vmName; + } + + public String getToCheckpointId() { + return toCheckpointId; + } + + public String getFromCheckpointId() { + return fromCheckpointId; + } + + public Long getFromCheckpointCreateTime() { + return fromCheckpointCreateTime; + } + + public String getSocket() { + return socket; + } + + public Map getDiskPathUuidMap() { + return diskPathUuidMap; + } + + public boolean isIncremental() { + return fromCheckpointId != null && !fromCheckpointId.isEmpty(); + } + + public boolean isStoppedVM() { + return stoppedVM; + } + + public Map getDiskPathPassphraseMap() { + return diskPathPassphraseMap; + } + + @Override + public boolean executeInSequence() { + return true; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/StartNBDServerAnswer.java b/core/src/main/java/org/apache/cloudstack/backup/StartNBDServerAnswer.java new file mode 100644 index 000000000000..d8c78d3c8807 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/StartNBDServerAnswer.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 +//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; + +public class StartNBDServerAnswer extends Answer { + private String imageTransferId; + private String transferUrl; + + public StartNBDServerAnswer() { + } + + public StartNBDServerAnswer(StartNBDServerCommand cmd, boolean success, String details) { + super(cmd, success, details); + } + + public StartNBDServerAnswer(StartNBDServerCommand cmd, boolean success, String details, + String imageTransferId, String transferUrl) { + super(cmd, success, details); + this.imageTransferId = imageTransferId; + this.transferUrl = transferUrl; + } + + public String getImageTransferId() { + return imageTransferId; + } + + public void setImageTransferId(String imageTransferId) { + this.imageTransferId = imageTransferId; + } + + public String getTransferUrl() { + return transferUrl; + } + + public void setTransferUrl(String transferUrl) { + this.transferUrl = transferUrl; + } + +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/StartNBDServerCommand.java b/core/src/main/java/org/apache/cloudstack/backup/StartNBDServerCommand.java new file mode 100644 index 000000000000..07e50f11fd37 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/StartNBDServerCommand.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 +//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 StartNBDServerCommand extends Command { + private String transferId; + private String exportName; + private String volumePath; + private String socket; + private String direction; + private String fromCheckpointId; + @LogLevel(LogLevel.Log4jLevel.Off) + private byte[] passphrase; + + public StartNBDServerCommand() { + } + + protected StartNBDServerCommand(String transferId, String exportName, String volumePath, String socket, String direction, String fromCheckpointId, byte[] passphrase) { + this.transferId = transferId; + this.socket = socket; + this.exportName = exportName; + this.volumePath = volumePath; + this.direction = direction; + this.fromCheckpointId = fromCheckpointId; + this.passphrase = passphrase; + } + + public String getExportName() { + return exportName; + } + + public String getSocket() { + return socket; + } + + public String getTransferId() { + return transferId; + } + + @Override + public boolean executeInSequence() { + return true; + } + + public String getVolumePath() { + return volumePath; + } + + public String getDirection() { + return direction; + } + + public String getFromCheckpointId() { + return fromCheckpointId; + } + + public byte[] getPassphrase() { + return passphrase; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/StopBackupAnswer.java b/core/src/main/java/org/apache/cloudstack/backup/StopBackupAnswer.java new file mode 100644 index 000000000000..ce977f31e005 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/StopBackupAnswer.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 +//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; + +public class StopBackupAnswer extends Answer { + + public StopBackupAnswer() { + } + + public StopBackupAnswer(StopBackupCommand cmd, boolean success, String details) { + super(cmd, success, details); + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/StopBackupCommand.java b/core/src/main/java/org/apache/cloudstack/backup/StopBackupCommand.java new file mode 100644 index 000000000000..d3055021e9de --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/StopBackupCommand.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 +//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; + +public class StopBackupCommand extends Command { + private String vmName; + private Long vmId; + private Long backupId; + + public StopBackupCommand() { + } + + public StopBackupCommand(String vmName, Long vmId, Long backupId) { + this.vmName = vmName; + this.vmId = vmId; + this.backupId = backupId; + } + + public String getVmName() { + return vmName; + } + + public Long getVmId() { + return vmId; + } + + public Long getBackupId() { + return backupId; + } + + @Override + public boolean executeInSequence() { + return true; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/StopNBDServerCommand.java b/core/src/main/java/org/apache/cloudstack/backup/StopNBDServerCommand.java new file mode 100644 index 000000000000..d75168a22eb2 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/StopNBDServerCommand.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 +//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; + +public class StopNBDServerCommand extends Command { + private String transferId; + private String direction; + + public StopNBDServerCommand() { + } + + public StopNBDServerCommand(String transferId, String direction) { + this.transferId = transferId; + this.direction = direction; + } + + public String getTransferId() { + return transferId; + } + + public String getDirection() { + return direction; + } + + @Override + public boolean executeInSequence() { + return true; + } +} 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 ecebd57a178c..34f8d7b8bcdd 100644 --- a/core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.java +++ b/core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.java @@ -21,6 +21,7 @@ import com.cloud.agent.api.Command; import com.cloud.agent.api.LogLevel; +import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; import java.util.List; @@ -29,11 +30,23 @@ public class TakeBackupCommand extends Command { private String backupPath; private String backupRepoType; private String backupRepoAddress; + private List volumePools; private List volumePaths; private Boolean quiesce; @LogLevel(LogLevel.Log4jLevel.Off) private String mountOptions; + // Incremental backup fields (NAS provider; null/empty for legacy full-only callers). + private String mode; // "full" or "incremental"; null => legacy behaviour (script default) + private String bitmapNew; // Checkpoint/bitmap name to create with this backup (timestamp-based) + private String bitmapParent; // Incremental: parent bitmap to read changes since + + // Per-volume parent backup file paths (one per VM volume, ordered by deviceId — same + // order as volumePaths). The script rebases each new qcow2 onto the matching parent. + // Backup file UUIDs differ across volumes, so a single parentPath would have rebased + // every data disk onto the root file. New callers MUST populate parentPaths. + private List parentPaths; + public TakeBackupCommand(String vmName, String backupPath) { super(); this.vmName = vmName; @@ -80,6 +93,14 @@ public void setMountOptions(String mountOptions) { this.mountOptions = mountOptions; } + public List getVolumePools() { + return volumePools; + } + + public void setVolumePools(List volumePools) { + this.volumePools = volumePools; + } + public List getVolumePaths() { return volumePaths; } @@ -96,6 +117,38 @@ public void setQuiesce(Boolean quiesce) { this.quiesce = quiesce; } + public String getMode() { + return mode; + } + + public void setMode(String mode) { + this.mode = mode; + } + + public String getBitmapNew() { + return bitmapNew; + } + + public void setBitmapNew(String bitmapNew) { + this.bitmapNew = bitmapNew; + } + + public String getBitmapParent() { + return bitmapParent; + } + + public void setBitmapParent(String bitmapParent) { + this.bitmapParent = bitmapParent; + } + + public List getParentPaths() { + return parentPaths; + } + + public void setParentPaths(List parentPaths) { + this.parentPaths = parentPaths; + } + @Override public boolean executeInSequence() { return true; diff --git a/core/src/main/java/org/apache/cloudstack/backup/TakeBackupHashCommand.java b/core/src/main/java/org/apache/cloudstack/backup/TakeBackupHashCommand.java new file mode 100644 index 000000000000..7effe40686b2 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/TakeBackupHashCommand.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.backup; + +import com.cloud.agent.api.Command; +import org.apache.cloudstack.storage.to.BackupDeltaTO; + +import java.util.List; + +public class TakeBackupHashCommand extends Command { + + private List backupDeltaTOList; + + private String backupUuid; + + public TakeBackupHashCommand(List backupDeltaTOList, String backupUuid) { + this.backupDeltaTOList = backupDeltaTOList; + this.backupUuid = backupUuid; + } + + public List getBackupDeltaTOList() { + return backupDeltaTOList; + } + + public String getBackupUuid() { + return backupUuid; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/TakeKbossBackupAnswer.java b/core/src/main/java/org/apache/cloudstack/backup/TakeKbossBackupAnswer.java new file mode 100644 index 000000000000..1827c766f573 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/TakeKbossBackupAnswer.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.backup; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.utils.Pair; +import com.cloud.utils.exception.BackupException; + +import java.util.Map; + +public class TakeKbossBackupAnswer extends Answer { + + private Map mapVolumeUuidToNewVolumePath; + private Map> mapVolumeUuidToDeltaPathOnSecondaryAndSize; + private boolean isVmConsistent = true; + + public TakeKbossBackupAnswer(Command command, boolean success, Map mapVolumeUuidToNewVolumePath, + Map> mapVolumeUuidToDeltaPathOnSecondaryAndSize) { + super(command, success, null); + this.mapVolumeUuidToNewVolumePath = mapVolumeUuidToNewVolumePath; + this.mapVolumeUuidToDeltaPathOnSecondaryAndSize = mapVolumeUuidToDeltaPathOnSecondaryAndSize; + } + + public TakeKbossBackupAnswer(Command command, Exception e) { + super(command, e); + if (e instanceof BackupException) { + this.isVmConsistent = ((BackupException)e).isVmConsistent(); + } + } + + public Map getMapVolumeUuidToNewVolumePath() { + return mapVolumeUuidToNewVolumePath; + } + + public Map> getMapVolumeUuidToDeltaPathOnSecondaryAndSize() { + return mapVolumeUuidToDeltaPathOnSecondaryAndSize; + } + + public boolean isVmConsistent() { + return isVmConsistent; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/TakeKbossBackupCommand.java b/core/src/main/java/org/apache/cloudstack/backup/TakeKbossBackupCommand.java new file mode 100644 index 000000000000..b145fa6257b1 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/TakeKbossBackupCommand.java @@ -0,0 +1,92 @@ +// +// 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 org.apache.cloudstack.storage.to.KbossTO; + +import java.util.List; + +public class TakeKbossBackupCommand extends Command { + + private boolean quiesceVm; + + private boolean runningVM; + + private boolean endChain; + + private String vmName; + + private String imageStoreUrl; + + private List backupChainImageStoreUrls; + + private List kbossTOS; + + private boolean isolated; + + public TakeKbossBackupCommand(boolean quiesceVm, boolean runningVM, boolean endChain, String vmName, String imageStoreUrl, List backupChainImageStoreUrls, List kbossTOS, boolean isolated) { + this.quiesceVm = quiesceVm; + this.runningVM = runningVM; + this.endChain = endChain; + this.vmName = vmName; + this.imageStoreUrl = imageStoreUrl; + this.backupChainImageStoreUrls = backupChainImageStoreUrls; + this.kbossTOS = kbossTOS; + this.isolated = isolated; + } + + public boolean isQuiesceVm() { + return quiesceVm; + } + + public boolean isRunningVM() { + return runningVM; + } + + public boolean isEndChain() { + return endChain; + } + + public String getVmName() { + return vmName; + } + + public String getImageStoreUrl() { + return imageStoreUrl; + } + + public List getBackupChainImageStoreUrls() { + return backupChainImageStoreUrls; + } + + public List getKbossTOs() { + return kbossTOS; + } + + public boolean isIsolated() { + return isolated; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/ValidateKbossVmAnswer.java b/core/src/main/java/org/apache/cloudstack/backup/ValidateKbossVmAnswer.java new file mode 100644 index 000000000000..fcff6a8cce8c --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/ValidateKbossVmAnswer.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.backup; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; + +public class ValidateKbossVmAnswer extends Answer { + + private boolean bootValidated; + private String screenshotPath; + private String scriptResult; + + public ValidateKbossVmAnswer(Command command, boolean bootValidated, String screenshotPath, String scriptResult) { + super(command); + this.bootValidated = bootValidated; + this.screenshotPath = screenshotPath; + this.scriptResult = scriptResult; + } + + public boolean isBootValidated() { + return bootValidated; + } + + public String getScreenshotPath() { + return screenshotPath; + } + + public String getScriptResult() { + return scriptResult; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/ValidateKbossVmCommand.java b/core/src/main/java/org/apache/cloudstack/backup/ValidateKbossVmCommand.java new file mode 100644 index 000000000000..9e10499f53d9 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/ValidateKbossVmCommand.java @@ -0,0 +1,133 @@ +// +// 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.to.VirtualMachineTO; +import org.apache.cloudstack.storage.to.BackupDeltaTO; + +public class ValidateKbossVmCommand extends Command { + + private VirtualMachineTO vm; + private BackupDeltaTO backupDeltaTO; + + private String scriptToExecute; + + private String scriptArguments; + private String expectedResult; + + private Integer scriptTimeout; + private Integer bootTimeout; + private Integer screenshotWait; + + private boolean takeScreenshot; + private boolean waitForBoot; + private boolean executeScript; + + public ValidateKbossVmCommand(VirtualMachineTO vm, BackupDeltaTO backupDeltaTO) { + this.vm = vm; + this.backupDeltaTO = backupDeltaTO; + } + + public void setScriptToExecute(String scriptToExecute) { + this.scriptToExecute = scriptToExecute; + } + + public void setScriptArguments(String scriptArguments) { + this.scriptArguments = scriptArguments; + } + + public void setExpectedResult(String expectedResult) { + this.expectedResult = expectedResult; + } + + public void setScriptTimeout(Integer scriptTimeout) { + this.scriptTimeout = scriptTimeout; + } + + public void setTakeScreenshot(boolean takeScreenshot) { + this.takeScreenshot = takeScreenshot; + } + + public void setWaitForBoot(boolean waitForBoot) { + this.waitForBoot = waitForBoot; + } + + public void setExecuteScript(boolean executeScript) { + this.executeScript = executeScript; + } + + public void setBootTimeout(Integer bootTimeout) { + this.bootTimeout = bootTimeout; + } + + public void setScreenshotWait(Integer screenshotWait) { + this.screenshotWait = screenshotWait; + } + + public VirtualMachineTO getVm() { + return vm; + } + + public BackupDeltaTO getBackupDeltaTO() { + return backupDeltaTO; + } + + public String getScriptToExecute() { + return scriptToExecute; + } + + public String getScriptArguments() { + return scriptArguments; + } + + public String getExpectedResult() { + return expectedResult; + } + + public Integer getScriptTimeout() { + return scriptTimeout; + } + + public Integer getBootTimeout() { + return bootTimeout; + } + + public Integer getScreenshotWait() { + return screenshotWait; + } + + public boolean isTakeScreenshot() { + return takeScreenshot; + } + + public boolean isWaitForBoot() { + return waitForBoot; + } + + public boolean isExecuteScript() { + return executeScript; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/direct/download/DirectTemplateDownloaderImpl.java b/core/src/main/java/org/apache/cloudstack/direct/download/DirectTemplateDownloaderImpl.java index d22c803818be..05619e5632b0 100644 --- a/core/src/main/java/org/apache/cloudstack/direct/download/DirectTemplateDownloaderImpl.java +++ b/core/src/main/java/org/apache/cloudstack/direct/download/DirectTemplateDownloaderImpl.java @@ -21,6 +21,7 @@ import com.cloud.utils.UriUtils; import com.cloud.utils.exception.CloudRuntimeException; import org.apache.cloudstack.utils.security.DigestHelper; +import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; @@ -33,6 +34,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.UUID; public abstract class DirectTemplateDownloaderImpl implements DirectTemplateDownloader { @@ -128,15 +130,14 @@ public void setFollowRedirects(boolean followRedirects) { */ protected File createTemporaryDirectoryAndFile(String downloadDir) { createFolder(downloadDir); - return new File(downloadDir + File.separator + getFileNameFromUrl()); + return new File(downloadDir + File.separator + getTemporaryFileName()); } /** - * Return filename from url + * Return filename from the temporary download file */ - public String getFileNameFromUrl() { - String[] urlParts = url.split("/"); - return urlParts[urlParts.length - 1]; + public String getTemporaryFileName() { + return String.format("%s.%s", UUID.randomUUID(), FilenameUtils.getExtension(url)); } @Override @@ -147,16 +148,16 @@ public boolean validateChecksum() { try { while (!valid && retry > 0) { retry--; - logger.info("Performing checksum validation for downloaded template " + templateId + " using " + checksum + ", retries left: " + retry); + logger.info("Performing checksum validation for downloaded Template " + templateId + " using " + checksum + ", retries left: " + retry); valid = DigestHelper.check(checksum, new FileInputStream(downloadedFilePath)); if (!valid && retry > 0) { - logger.info("Checksum validation failed, re-downloading template"); + logger.info("Checksum validation failed, re-downloading Template"); redownload = true; resetDownloadFile(); downloadTemplate(); } } - logger.info("Checksum validation for template " + templateId + ": " + (valid ? "succeeded" : "failed")); + logger.info("Checksum validation for Template " + templateId + ": " + (valid ? "succeeded" : "failed")); return valid; } catch (IOException e) { throw new CloudRuntimeException("could not check sum for file: " + downloadedFilePath, e); @@ -173,7 +174,7 @@ public boolean validateChecksum() { */ private void resetDownloadFile() { File f = new File(getDownloadedFilePath()); - logger.info("Resetting download file: " + getDownloadedFilePath() + ", in order to re-download and persist template " + templateId + " on it"); + logger.info("Resetting download file: " + getDownloadedFilePath() + ", in order to re-download and persist Template " + templateId + " on it"); try { if (f.exists()) { f.delete(); diff --git a/core/src/main/java/org/apache/cloudstack/direct/download/HttpDirectTemplateDownloader.java b/core/src/main/java/org/apache/cloudstack/direct/download/HttpDirectTemplateDownloader.java index 8c4147fad606..99b84bb645c8 100644 --- a/core/src/main/java/org/apache/cloudstack/direct/download/HttpDirectTemplateDownloader.java +++ b/core/src/main/java/org/apache/cloudstack/direct/download/HttpDirectTemplateDownloader.java @@ -19,6 +19,7 @@ package org.apache.cloudstack.direct.download; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -32,6 +33,7 @@ import com.cloud.utils.Pair; import com.cloud.utils.UriUtils; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.net.HttpClientCloudStackUserAgent; import com.cloud.utils.storage.QCOW2Utils; import org.apache.commons.collections.MapUtils; import org.apache.commons.httpclient.HttpClient; @@ -39,6 +41,7 @@ import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.HeadMethod; +import org.apache.commons.httpclient.params.HttpMethodParams; import org.apache.commons.io.IOUtils; public class HttpDirectTemplateDownloader extends DirectTemplateDownloaderImpl { @@ -68,6 +71,7 @@ public HttpDirectTemplateDownloader(String url, Long templateId, String destPool protected GetMethod createRequest(String downloadUrl, Map headers) { GetMethod request = new GetMethod(downloadUrl); request.setFollowRedirects(this.isFollowRedirects()); + request.getParams().setParameter(HttpMethodParams.USER_AGENT, HttpClientCloudStackUserAgent.CLOUDSTACK_USER_AGENT); if (MapUtils.isNotEmpty(headers)) { for (String key : headers.keySet()) { request.setRequestHeader(key, headers.get(key)); @@ -82,7 +86,7 @@ public Pair downloadTemplate() { try { int status = client.executeMethod(request); if (status != HttpStatus.SC_OK) { - logger.warn("Not able to download template, status code: " + status); + logger.warn("Not able to download Template, status code: " + status); return new Pair<>(false, null); } return performDownload(); @@ -94,14 +98,14 @@ public Pair downloadTemplate() { } protected Pair performDownload() { - logger.info("Downloading template " + getTemplateId() + " from " + getUrl() + " to: " + getDownloadedFilePath()); + logger.info("Downloading Template " + getTemplateId() + " from " + getUrl() + " to: " + getDownloadedFilePath()); try ( InputStream in = request.getResponseBodyAsStream(); OutputStream out = new FileOutputStream(getDownloadedFilePath()) ) { IOUtils.copy(in, out); } catch (IOException e) { - logger.error("Error downloading template " + getTemplateId() + " due to: " + e.getMessage()); + logger.error("Error downloading Template " + getTemplateId() + " due to: " + e.getMessage()); return new Pair<>(false, null); } return new Pair<>(true, getDownloadedFilePath()); @@ -111,6 +115,7 @@ protected Pair performDownload() { public boolean checkUrl(String url) { HeadMethod httpHead = new HeadMethod(url); httpHead.setFollowRedirects(this.isFollowRedirects()); + httpHead.getParams().setParameter(HttpMethodParams.USER_AGENT, HttpClientCloudStackUserAgent.CLOUDSTACK_USER_AGENT); try { int responseCode = client.executeMethod(httpHead); if (responseCode != HttpStatus.SC_OK) { 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 b84718e4f932..34ab94d55b19 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 @@ -149,7 +149,7 @@ public Pair downloadTemplate() { * Consume response and persist it on getDownloadedFilePath() file */ protected Pair consumeResponse(CloseableHttpResponse response) { - logger.info("Downloading template " + getTemplateId() + " from " + getUrl() + " to: " + getDownloadedFilePath()); + logger.info("Downloading Template " + getTemplateId() + " from " + getUrl() + " to: " + getDownloadedFilePath()); if (response.getStatusLine().getStatusCode() != 200) { throw new CloudRuntimeException("Error on HTTPS response"); } @@ -159,7 +159,7 @@ protected Pair consumeResponse(CloseableHttpResponse response) OutputStream out = new FileOutputStream(getDownloadedFilePath()); IOUtils.copy(in, out); } catch (Exception e) { - logger.error("Error parsing response for template " + getTemplateId() + " due to: " + e.getMessage()); + logger.error("Error parsing response for Template " + getTemplateId() + " due to: " + e.getMessage()); return new Pair<>(false, null); } return new Pair<>(true, getDownloadedFilePath()); diff --git a/core/src/main/java/org/apache/cloudstack/direct/download/MetalinkDirectTemplateDownloader.java b/core/src/main/java/org/apache/cloudstack/direct/download/MetalinkDirectTemplateDownloader.java index 5335da991506..854c310cde9a 100644 --- a/core/src/main/java/org/apache/cloudstack/direct/download/MetalinkDirectTemplateDownloader.java +++ b/core/src/main/java/org/apache/cloudstack/direct/download/MetalinkDirectTemplateDownloader.java @@ -72,10 +72,10 @@ public MetalinkDirectTemplateDownloader(String url, String destPoolPath, Long te metalinkUrls = downloader.getMetalinkUrls(url); metalinkChecksums = downloader.getMetalinkChecksums(url); if (CollectionUtils.isEmpty(metalinkUrls)) { - logger.error(String.format("No urls found on metalink file: %s. Not possible to download template %s ", url, templateId)); + logger.error(String.format("No URLs found on metalink file: %s. Not possible to download Template %s ", url, templateId)); } else { setUrl(metalinkUrls.get(0)); - logger.info("Metalink downloader created, metalink url: " + url + " parsed - " + + logger.info("Metalink downloader created, metalink URL: " + url + " parsed - " + metalinkUrls.size() + " urls and " + (CollectionUtils.isNotEmpty(metalinkChecksums) ? metalinkChecksums.size() : "0") + " checksums found"); } @@ -93,11 +93,11 @@ public Pair downloadTemplate() { if (!isRedownload()) { setUrl(metalinkUrls.get(i)); } - logger.info("Trying to download template from url: " + getUrl()); + logger.info("Trying to download Template from URL: " + getUrl()); DirectTemplateDownloader urlDownloader = createDownloaderForMetalinks(getUrl(), getTemplateId(), getDestPoolPath(), getChecksum(), headers, connectTimeout, soTimeout, null, temporaryDownloadPath); try { - setDownloadedFilePath(downloadDir + File.separator + getFileNameFromUrl()); + setDownloadedFilePath(downloadDir + File.separator + getTemporaryFileName()); File f = new File(getDownloadedFilePath()); if (f.exists()) { f.delete(); @@ -106,10 +106,10 @@ public Pair downloadTemplate() { Pair downloadResult = urlDownloader.downloadTemplate(); downloaded = downloadResult.first(); if (downloaded) { - logger.info("Successfully downloaded template from url: " + getUrl()); + logger.info("Successfully downloaded Template from URL: " + getUrl()); } } catch (Exception e) { - logger.error(String.format("Error downloading template: %s from URL: %s due to: %s", getTemplateId(), getUrl(), e.getMessage()), e); + logger.error(String.format("Error downloading Template: %s from URL: %s due to: %s", getTemplateId(), getUrl(), e.getMessage()), e); } i++; } diff --git a/core/src/main/java/org/apache/cloudstack/direct/download/NfsDirectTemplateDownloader.java b/core/src/main/java/org/apache/cloudstack/direct/download/NfsDirectTemplateDownloader.java index 21184ef07fe9..6b0959b78ffb 100644 --- a/core/src/main/java/org/apache/cloudstack/direct/download/NfsDirectTemplateDownloader.java +++ b/core/src/main/java/org/apache/cloudstack/direct/download/NfsDirectTemplateDownloader.java @@ -69,7 +69,7 @@ public Pair downloadTemplate() { String mount = String.format(mountCommand, srcHost + ":" + srcPath, "/mnt/" + mountSrcUuid); Script.runSimpleBashScript(mount); String downloadDir = getDestPoolPath() + File.separator + getDirectDownloadTempPath(getTemplateId()); - setDownloadedFilePath(downloadDir + File.separator + getFileNameFromUrl()); + setDownloadedFilePath(downloadDir + File.separator + getTemporaryFileName()); Script.runSimpleBashScript("cp /mnt/" + mountSrcUuid + srcPath + " " + getDownloadedFilePath()); Script.runSimpleBashScript("umount /mnt/" + mountSrcUuid); return new Pair<>(true, getDownloadedFilePath()); diff --git a/core/src/main/java/org/apache/cloudstack/storage/clvm/command/ClvmLockTransferAnswer.java b/core/src/main/java/org/apache/cloudstack/storage/clvm/command/ClvmLockTransferAnswer.java new file mode 100644 index 000000000000..f3c43c400b2b --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/storage/clvm/command/ClvmLockTransferAnswer.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.storage.clvm.command; + +import com.cloud.agent.api.Answer; + +/** + * Answer for ClvmLockTransferCommand, containing lock state information. + * This answer includes the current lock holder information when querying lock state. + */ +public class ClvmLockTransferAnswer extends Answer { + + private String currentLockHostname; + private boolean isActive; + private boolean isOpen; + private String lvAttributes; + + public ClvmLockTransferAnswer(ClvmLockTransferCommand cmd, boolean result, String details) { + super(cmd, result, details); + } + + public ClvmLockTransferAnswer(ClvmLockTransferCommand cmd, boolean result, String details, + String currentLockHostname, boolean isActive, boolean isOpen, + String lvAttributes) { + super(cmd, result, details); + this.currentLockHostname = currentLockHostname; + this.isActive = isActive; + this.isOpen = isOpen; + this.lvAttributes = lvAttributes; + } + + /** + * Get the hostname from lv_host. Retained for diagnostics only — + * do NOT use this to determine lock holder identity. + */ + public String getCurrentLockHostname() { + return currentLockHostname; + } + + public void setCurrentLockHostname(String currentLockHostname) { + this.currentLockHostname = currentLockHostname; + } + + /** + * Whether the LV is locally active on the queried host (lv_attr[4]=='a'). + * This is the authoritative signal for lock holder discovery via fan-out. + */ + public boolean isActive() { + return isActive; + } + + public void setActive(boolean active) { + isActive = active; + } + + /** + * Whether a process has the device file open on the queried host (lv_attr[5]=='o'). + * true means a VM is actively doing I/O on this host right now — do NOT deactivate. + */ + public boolean isOpen() { + return isOpen; + } + + public void setOpen(boolean open) { + isOpen = open; + } + + public String getLvAttributes() { + return lvAttributes; + } + + public void setLvAttributes(String lvAttributes) { + this.lvAttributes = lvAttributes; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/storage/clvm/command/ClvmLockTransferCommand.java b/core/src/main/java/org/apache/cloudstack/storage/clvm/command/ClvmLockTransferCommand.java new file mode 100644 index 000000000000..218fb798869d --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/storage/clvm/command/ClvmLockTransferCommand.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.storage.clvm.command; + +import com.cloud.agent.api.Command; + +/** + * Command to transfer CLVM (Clustered LVM) exclusive lock between hosts. + * This enables lightweight volume migration for CLVM storage pools where volumes + * reside in the same Volume Group (VG) but need to be accessed from different hosts. + * + *

Instead of copying volume data (traditional migration), this command simply + * deactivates the LV on the source host and activates it exclusively on the destination host. + * + *

This is significantly faster (10-100x) than traditional migration and uses no network bandwidth. + */ +public class ClvmLockTransferCommand extends Command { + + /** + * Operation to perform on the CLVM volume. + * Maps to lvchange flags for LVM operations. + */ + public enum Operation { + /** Deactivate the volume on this host (-an) */ + DEACTIVATE("-an", "deactivate"), + + /** Activate the volume exclusively on this host (-aey) */ + ACTIVATE_EXCLUSIVE("-aey", "activate exclusively"), + + /** Activate the volume in shared mode on this host (-asy) */ + ACTIVATE_SHARED("-asy", "activate in shared mode"), + + /** Query the current lock state (lvs -o lv_attr,lv_host) */ + QUERY_LOCK_STATE("query", "query lock state"); + + private final String lvchangeFlag; + private final String description; + + Operation(String lvchangeFlag, String description) { + this.lvchangeFlag = lvchangeFlag; + this.description = description; + } + + public String getLvchangeFlag() { + return lvchangeFlag; + } + + public String getDescription() { + return description; + } + } + + private String lvPath; + private Operation operation; + private String volumeUuid; + + public ClvmLockTransferCommand() { + // For serialization + } + + public ClvmLockTransferCommand(Operation operation, String lvPath, String volumeUuid) { + this.operation = operation; + this.lvPath = lvPath; + this.volumeUuid = volumeUuid; + setWait(65); + } + + public String getLvPath() { + return lvPath; + } + + public Operation getOperation() { + return operation; + } + + public String getVolumeUuid() { + return volumeUuid; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/HostTest.java b/core/src/main/java/org/apache/cloudstack/storage/command/BackupDeleteAnswer.java similarity index 63% rename from framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/HostTest.java rename to core/src/main/java/org/apache/cloudstack/storage/command/BackupDeleteAnswer.java index 87aae7788e27..6cc48ea296d1 100644 --- a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/HostTest.java +++ b/core/src/main/java/org/apache/cloudstack/storage/command/BackupDeleteAnswer.java @@ -1,3 +1,4 @@ +// // 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 @@ -14,21 +15,22 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. +// +package org.apache.cloudstack.storage.command; -package org.apache.cloudstack.quota.activationrule.presetvariables; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; +public class BackupDeleteAnswer extends Answer { -@RunWith(MockitoJUnitRunner.class) -public class HostTest { + private long backupId; + + public BackupDeleteAnswer(Command command, boolean success, String details) { + super(command, success, details); + backupId = ((DeleteCommand) command).getData().getId(); + } - @Test - public void setTagsTestAddFieldTagsToCollection() { - Host variable = new Host(); - variable.setTags(null); - Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("tags")); + public long getBackupId() { + return backupId; } } diff --git a/core/src/main/java/org/apache/cloudstack/storage/command/CheckDataStoreStoragePolicyComplainceCommand.java b/core/src/main/java/org/apache/cloudstack/storage/command/CheckDataStoreStoragePolicyComplianceCommand.java similarity index 93% rename from core/src/main/java/org/apache/cloudstack/storage/command/CheckDataStoreStoragePolicyComplainceCommand.java rename to core/src/main/java/org/apache/cloudstack/storage/command/CheckDataStoreStoragePolicyComplianceCommand.java index f9544b873ef2..4544e98451f3 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/command/CheckDataStoreStoragePolicyComplainceCommand.java +++ b/core/src/main/java/org/apache/cloudstack/storage/command/CheckDataStoreStoragePolicyComplianceCommand.java @@ -21,12 +21,12 @@ import com.cloud.agent.api.to.StorageFilerTO; -public class CheckDataStoreStoragePolicyComplainceCommand extends StorageSubSystemCommand { +public class CheckDataStoreStoragePolicyComplianceCommand extends StorageSubSystemCommand { String storagePolicyId; private StorageFilerTO storagePool; - public CheckDataStoreStoragePolicyComplainceCommand(String storagePolicyId, StorageFilerTO storagePool) { + public CheckDataStoreStoragePolicyComplianceCommand(String storagePolicyId, StorageFilerTO storagePool) { super(); this.storagePolicyId = storagePolicyId; diff --git a/core/src/main/java/org/apache/cloudstack/storage/command/DeleteCommand.java b/core/src/main/java/org/apache/cloudstack/storage/command/DeleteCommand.java index 6f82fa97818d..9aa6f26b5d9c 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/command/DeleteCommand.java +++ b/core/src/main/java/org/apache/cloudstack/storage/command/DeleteCommand.java @@ -24,6 +24,8 @@ public final class DeleteCommand extends StorageSubSystemCommand { private DataTO data; + private boolean deleteChain; + public DeleteCommand(final DataTO data) { super(); this.data = data; @@ -42,6 +44,14 @@ public DataTO getData() { return data; } + public void setDeleteChain(boolean deleteChain) { + this.deleteChain = deleteChain; + } + + public boolean isDeleteChain() { + return deleteChain; + } + @Override public void setExecuteInSequence(final boolean inSeq) { diff --git a/core/src/main/java/org/apache/cloudstack/storage/command/RevertSnapshotCommand.java b/core/src/main/java/org/apache/cloudstack/storage/command/RevertSnapshotCommand.java index 174302252a55..42926d37cdfe 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/command/RevertSnapshotCommand.java +++ b/core/src/main/java/org/apache/cloudstack/storage/command/RevertSnapshotCommand.java @@ -25,6 +25,8 @@ public final class RevertSnapshotCommand extends StorageSubSystemCommand { private SnapshotObjectTO dataOnPrimaryStorage; private boolean _executeInSequence = false; + private boolean deleteChain; + public RevertSnapshotCommand(SnapshotObjectTO data, SnapshotObjectTO dataOnPrimaryStorage) { super(); this.data = data; @@ -43,6 +45,14 @@ public SnapshotObjectTO getDataOnPrimaryStorage() { return dataOnPrimaryStorage; } + public boolean isDeleteChain() { + return deleteChain; + } + + public void setDeleteChain(boolean deleteChain) { + this.deleteChain = deleteChain; + } + @Override public void setExecuteInSequence(final boolean executeInSequence) { _executeInSequence = executeInSequence; diff --git a/core/src/main/java/org/apache/cloudstack/storage/command/TemplateOrVolumePostUploadCommand.java b/core/src/main/java/org/apache/cloudstack/storage/command/TemplateOrVolumePostUploadCommand.java index 3ac83031eaf5..98db75f39025 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/command/TemplateOrVolumePostUploadCommand.java +++ b/core/src/main/java/org/apache/cloudstack/storage/command/TemplateOrVolumePostUploadCommand.java @@ -19,6 +19,9 @@ package org.apache.cloudstack.storage.command; +import com.cloud.configuration.Resource; +import org.apache.cloudstack.utils.bytescale.ByteScaleUtils; + public class TemplateOrVolumePostUploadCommand { long entityId; @@ -57,8 +60,10 @@ public class TemplateOrVolumePostUploadCommand { private String nfsVersion; - public TemplateOrVolumePostUploadCommand(long entityId, String entityUUID, String absolutePath, String checksum, String type, String name, String imageFormat, String dataTo, - String dataToRole) { + private long zoneId; + + public TemplateOrVolumePostUploadCommand(long entityId, String entityUUID, String absolutePath, String checksum, + String type, String name, String imageFormat, String dataTo, String dataToRole, long zoneId) { this.entityId = entityId; this.entityUUID = entityUUID; this.absolutePath = absolutePath; @@ -68,9 +73,7 @@ public TemplateOrVolumePostUploadCommand(long entityId, String entityUUID, Strin this.imageFormat = imageFormat; this.dataTo = dataTo; this.dataToRole = dataToRole; - } - - public TemplateOrVolumePostUploadCommand() { + this.zoneId = zoneId; } public String getRemoteEndPoint() { @@ -185,6 +188,11 @@ public void setDescription(String description) { this.description = description; } + public void setDefaultMaxSecondaryStorageInBytes(long defaultMaxSecondaryStorageInBytes) { + this.defaultMaxSecondaryStorageInGB = defaultMaxSecondaryStorageInBytes != Resource.RESOURCE_UNLIMITED ? + ByteScaleUtils.bytesToGibibytes(defaultMaxSecondaryStorageInBytes) : Resource.RESOURCE_UNLIMITED; + } + public void setDefaultMaxSecondaryStorageInGB(long defaultMaxSecondaryStorageInGB) { this.defaultMaxSecondaryStorageInGB = defaultMaxSecondaryStorageInGB; } @@ -216,4 +224,8 @@ public void setProcessTimeout(long processTimeout) { public long getProcessTimeout() { return processTimeout; } + + public long getZoneId() { + return zoneId; + } } diff --git a/core/src/main/java/org/apache/cloudstack/storage/command/UploadStatusCommand.java b/core/src/main/java/org/apache/cloudstack/storage/command/UploadStatusCommand.java index 9e6b76e467ff..f78744046f73 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/command/UploadStatusCommand.java +++ b/core/src/main/java/org/apache/cloudstack/storage/command/UploadStatusCommand.java @@ -28,6 +28,7 @@ public enum EntityType { } private String entityUuid; private EntityType entityType; + private Boolean abort; protected UploadStatusCommand() { } @@ -37,6 +38,11 @@ public UploadStatusCommand(String entityUuid, EntityType entityType) { this.entityType = entityType; } + public UploadStatusCommand(String entityUuid, EntityType entityType, Boolean abort) { + this(entityUuid, entityType); + this.abort = abort; + } + public String getEntityUuid() { return entityUuid; } @@ -45,6 +51,10 @@ public EntityType getEntityType() { return entityType; } + public Boolean getAbort() { + return abort; + } + @Override public boolean executeInSequence() { return false; diff --git a/core/src/main/java/org/apache/cloudstack/storage/to/BackupDeltaTO.java b/core/src/main/java/org/apache/cloudstack/storage/to/BackupDeltaTO.java new file mode 100644 index 000000000000..662a2d486560 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/storage/to/BackupDeltaTO.java @@ -0,0 +1,102 @@ +// 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.storage.to; + +import com.cloud.agent.api.to.DataObjectType; +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.storage.Storage; +import org.apache.commons.lang3.builder.ReflectionToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +public class BackupDeltaTO implements DataTO { + private DataStoreTO dataStoreTO; + + private Hypervisor.HypervisorType hypervisorType; + + private String path; + + private String screenshotPath; + + private Storage.ImageFormat format; + + // When set, represents the Backup ID, not the delta ID. + private long id = 0; + + public BackupDeltaTO(DataStoreTO dataStoreTO, Hypervisor.HypervisorType hypervisorType, String path) { + this.dataStoreTO = dataStoreTO; + this.hypervisorType = hypervisorType; + this.path = path; + this.format = Storage.ImageFormat.QCOW2; + } + + public BackupDeltaTO(long id, DataStoreTO dataStoreTO, Hypervisor.HypervisorType hypervisorType, String path) { + this(dataStoreTO, hypervisorType, path); + this.id = id; + } + + @Override + public DataObjectType getObjectType() { + return DataObjectType.BACKUP; + } + + @Override + public DataStoreTO getDataStore() { + return dataStoreTO; + } + + @Override + public Hypervisor.HypervisorType getHypervisorType() { + return hypervisorType; + } + + @Override + public String getPath() { + return path; + } + + @Override + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public Storage.ImageFormat getFormat() { + return this.format; + } + + public void setScreenshotPath(String screenshotPath) { + this.screenshotPath = screenshotPath; + } + + public String getScreenshotPath() { + return screenshotPath; + } + + public void setPath(String path) { + this.path = path; + } + + @Override + public String toString() { + return new ReflectionToStringBuilder(this, ToStringStyle.JSON_STYLE).setExcludeFieldNames("id").toString(); + } +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/SnapshotMergeTreeTO.java b/core/src/main/java/org/apache/cloudstack/storage/to/DeltaMergeTreeTO.java similarity index 71% rename from core/src/main/java/com/cloud/agent/api/storage/SnapshotMergeTreeTO.java rename to core/src/main/java/org/apache/cloudstack/storage/to/DeltaMergeTreeTO.java index 78f23105e192..143cf6fe22bb 100644 --- a/core/src/main/java/com/cloud/agent/api/storage/SnapshotMergeTreeTO.java +++ b/core/src/main/java/org/apache/cloudstack/storage/to/DeltaMergeTreeTO.java @@ -16,28 +16,40 @@ * specific language governing permissions and limitations * under the License. */ -package com.cloud.agent.api.storage; +package org.apache.cloudstack.storage.to; import com.cloud.agent.api.to.DataTO; import org.apache.commons.lang3.builder.ReflectionToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; import java.util.List; -public class SnapshotMergeTreeTO { +public class DeltaMergeTreeTO { + + VolumeObjectTO volumeObjectTO; DataTO parent; DataTO child; List grandChildren; - public SnapshotMergeTreeTO(DataTO parent, DataTO child, List grandChildren) { + public DeltaMergeTreeTO(VolumeObjectTO volumeObjectTO, DataTO parent, DataTO child, List grandChildren) { + this.volumeObjectTO = volumeObjectTO; this.parent = parent; this.child = child; this.grandChildren = grandChildren; } + public VolumeObjectTO getVolumeObjectTO() { + return volumeObjectTO; + } + public DataTO getParent() { return parent; } + public void setParent(DataTO parent) { + this.parent = parent; + } + public DataTO getChild() { return child; } @@ -52,6 +64,6 @@ public void addGrandChild(DataTO grandChild) { @Override public String toString() { - return ReflectionToStringBuilder.toString(this); + return ReflectionToStringBuilder.toString(this, ToStringStyle.JSON_STYLE); } } diff --git a/core/src/main/java/org/apache/cloudstack/storage/to/KbossTO.java b/core/src/main/java/org/apache/cloudstack/storage/to/KbossTO.java new file mode 100644 index 000000000000..0280ffa27a1f --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/storage/to/KbossTO.java @@ -0,0 +1,101 @@ +package org.apache.cloudstack.storage.to; +// 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 java.util.List; +import java.util.stream.Collectors; + +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; +import org.apache.commons.lang3.builder.ReflectionToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +public class KbossTO { + + private String pathBackupParentOnSecondary; + private VolumeObjectTO volumeObjectTO; + private String deltaPathOnPrimary; + private String parentDeltaPathOnPrimary; + private String deltaPathOnSecondary; + + private DeltaMergeTreeTO deltaMergeTreeTO; + + private List vmSnapshotDeltaPaths; + + public KbossTO(VolumeObjectTO volumeObjectTO, List snapshotDataStoreVOs) { + this.volumeObjectTO = volumeObjectTO; + this.vmSnapshotDeltaPaths = snapshotDataStoreVOs.stream().map(SnapshotDataStoreVO::getInstallPath).collect(Collectors.toList()); + } + + public KbossTO(VolumeObjectTO volumeObjectTO, String deltaPathOnPrimary, String deltaPathOnSecondary) { + this.volumeObjectTO = volumeObjectTO; + this.deltaPathOnPrimary = deltaPathOnPrimary; + this.deltaPathOnSecondary = deltaPathOnSecondary; + } + + public String getPathBackupParentOnSecondary() { + return pathBackupParentOnSecondary; + } + + public VolumeObjectTO getVolumeObjectTO() { + return volumeObjectTO; + } + + public DeltaMergeTreeTO getDeltaMergeTreeTO() { + return deltaMergeTreeTO; + } + + public List getVmSnapshotDeltaPaths() { + return vmSnapshotDeltaPaths; + } + + public String getDeltaPathOnPrimary() { + return deltaPathOnPrimary; + } + + public String getDeltaPathOnSecondary() { + return deltaPathOnSecondary; + } + + public String getParentDeltaPathOnPrimary() { + return parentDeltaPathOnPrimary; + } + + public void setParentDeltaPathOnPrimary(String parentDeltaPathOnPrimary) { + this.parentDeltaPathOnPrimary = parentDeltaPathOnPrimary; + } + + public void setPathBackupParentOnSecondary(String pathBackupParentOnSecondary) { + this.pathBackupParentOnSecondary = pathBackupParentOnSecondary; + } + + public void setDeltaMergeTreeTO(DeltaMergeTreeTO deltaMergeTreeTO) { + this.deltaMergeTreeTO = deltaMergeTreeTO; + } + + public void setDeltaPathOnPrimary(String deltaPathOnPrimary) { + this.deltaPathOnPrimary = deltaPathOnPrimary; + } + + public void setDeltaPathOnSecondary(String deltaPathOnSecondary) { + this.deltaPathOnSecondary = deltaPathOnSecondary; + } + + @Override + public String toString() { + return ReflectionToStringBuilder.toString(this, ToStringStyle.JSON_STYLE); + } +} 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 0c3bb99e75ca..7643f80bbaac 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 @@ -48,9 +48,7 @@ public class SnapshotObjectTO extends DownloadableObjectTO implements DataTO { private Long physicalSize = (long) 0; private long accountId; - public SnapshotObjectTO() { - } @Override 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 92f9ee497a44..df98149faab6 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 @@ -32,11 +32,12 @@ import com.cloud.storage.Volume; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; +import java.io.Serializable; import java.util.Arrays; import java.util.List; import java.util.Set; -public class VolumeObjectTO extends DownloadableObjectTO implements DataTO { +public class VolumeObjectTO extends DownloadableObjectTO implements DataTO, Serializable { private String uuid; private Volume.Type volumeType; private DataStoreTO dataStore; @@ -116,8 +117,8 @@ public VolumeObjectTO(VolumeInfo volume) { iopsWriteRate = volume.getIopsWriteRate(); iopsWriteRateMax = volume.getIopsWriteRateMax(); iopsWriteRateMaxLength = volume.getIopsWriteRateMaxLength(); - cacheMode = volume.getCacheMode(); hypervisorType = volume.getHypervisorType(); + setCacheMode(volume.getCacheMode()); setDeviceId(volume.getDeviceId()); this.migrationOptions = volume.getMigrationOptions(); this.directDownload = volume.isDirectDownload(); @@ -343,6 +344,10 @@ public void setDeviceId(Long deviceId) { } public void setCacheMode(DiskCacheMode cacheMode) { + if (DiskCacheMode.HYPERVISOR_DEFAULT.equals(cacheMode) && !Hypervisor.HypervisorType.KVM.equals(hypervisorType)) { + this.cacheMode = DiskCacheMode.NONE; + return; + } this.cacheMode = cacheMode; } diff --git a/core/src/main/resources/META-INF/cloudstack/backup/spring-core-lifecycle-backup-context-inheritable.xml b/core/src/main/resources/META-INF/cloudstack/backup/spring-core-lifecycle-backup-context-inheritable.xml index 175d45e26752..fcbcb18c2bdf 100644 --- a/core/src/main/resources/META-INF/cloudstack/backup/spring-core-lifecycle-backup-context-inheritable.xml +++ b/core/src/main/resources/META-INF/cloudstack/backup/spring-core-lifecycle-backup-context-inheritable.xml @@ -29,4 +29,9 @@ + + + + + diff --git a/core/src/main/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml b/core/src/main/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml index 01c568d78916..cf43b8527a97 100644 --- a/core/src/main/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml +++ b/core/src/main/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml @@ -339,6 +339,10 @@ class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry"> + + + @@ -366,4 +370,9 @@ + + + + + diff --git a/plugins/host-allocators/random/src/main/resources/META-INF/cloudstack/host-allocator-random/module.properties b/core/src/main/resources/META-INF/cloudstack/dns/module.properties similarity index 94% rename from plugins/host-allocators/random/src/main/resources/META-INF/cloudstack/host-allocator-random/module.properties rename to core/src/main/resources/META-INF/cloudstack/dns/module.properties index dcfe8d3537ff..a2bb467be751 100644 --- a/plugins/host-allocators/random/src/main/resources/META-INF/cloudstack/host-allocator-random/module.properties +++ b/core/src/main/resources/META-INF/cloudstack/dns/module.properties @@ -1,3 +1,4 @@ +# # 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 @@ -14,5 +15,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -name=host-allocator-random -parent=allocator +# + +name=dns +parent=core diff --git a/core/src/main/resources/META-INF/cloudstack/dns/spring-core-lifecycle-dns-context-inheritable.xml b/core/src/main/resources/META-INF/cloudstack/dns/spring-core-lifecycle-dns-context-inheritable.xml new file mode 100644 index 000000000000..27cac9400284 --- /dev/null +++ b/core/src/main/resources/META-INF/cloudstack/dns/spring-core-lifecycle-dns-context-inheritable.xml @@ -0,0 +1,31 @@ + + + + + + + + diff --git a/core/src/main/resources/META-INF/cloudstack/kms/module.properties b/core/src/main/resources/META-INF/cloudstack/kms/module.properties new file mode 100644 index 000000000000..98e38d7cd8f6 --- /dev/null +++ b/core/src/main/resources/META-INF/cloudstack/kms/module.properties @@ -0,0 +1,21 @@ +# +# 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=kms +parent=core diff --git a/core/src/main/resources/META-INF/cloudstack/kms/spring-core-lifecycle-kms-context-inheritable.xml b/core/src/main/resources/META-INF/cloudstack/kms/spring-core-lifecycle-kms-context-inheritable.xml new file mode 100644 index 000000000000..9226eef8fc1a --- /dev/null +++ b/core/src/main/resources/META-INF/cloudstack/kms/spring-core-lifecycle-kms-context-inheritable.xml @@ -0,0 +1,29 @@ + + + + + + + + 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 6d4c6234c424..4ddadab999a4 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 @@ -235,7 +235,7 @@ protected LoadBalancerConfigCommand generateLoadBalancerConfigCommand() { lbs.toArray(arrayLbs); final NicTO nic = new NicTO(); - final LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(arrayLbs, "64.10.2.10", "10.1.10.2", "192.168.1.2", nic, null, "1000", false); + final LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(arrayLbs, "64.10.2.10", "10.1.10.2", "192.168.1.2", nic, null, "1000", false, 0L); cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, "10.1.10.2"); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); diff --git a/core/src/test/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java index 201242564ba6..ed819bb7c68f 100644 --- a/core/src/test/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java +++ b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java @@ -417,8 +417,6 @@ private void verifyArgs(final SetNetworkACLCommand cmd, final String script, fin // FIXME Check the json content assertEquals(VRScripts.UPDATE_CONFIG, script); assertEquals(VRScripts.NETWORK_ACL_CONFIG, args); - // assertEquals(args, " -d eth3 -M 01:23:45:67:89:AB -i 192.168.1.1 -m 24 -a Egress:ALL:0:0:192.168.0.1/24-192.168.0.2/24:ACCEPT:," + - // "Ingress:ICMP:0:0:192.168.0.1/24-192.168.0.2/24:DROP:,Ingress:TCP:20:80:192.168.0.1/24-192.168.0.2/24:ACCEPT:,"); break; case 2: assertEquals(VRScripts.UPDATE_CONFIG, script); @@ -464,8 +462,6 @@ protected SetupGuestNetworkCommand generateSetupGuestNetworkCommand() { private void verifyArgs(final SetupGuestNetworkCommand cmd, final String script, final String args) { // TODO Check the contents of the json file - //assertEquals(script, VRScripts.VPC_GUEST_NETWORK); - //assertEquals(args, " -C -M 01:23:45:67:89:AB -d eth4 -i 10.1.1.2 -g 10.1.1.1 -m 24 -n 10.1.1.0 -s 8.8.8.8,8.8.4.4 -e cloud.test"); } @Test @@ -783,7 +779,7 @@ protected LoadBalancerConfigCommand generateLoadBalancerConfigCommand1() { final LoadBalancerTO[] arrayLbs = new LoadBalancerTO[lbs.size()]; lbs.toArray(arrayLbs); final NicTO nic = new NicTO(); - final LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(arrayLbs, "64.10.2.10", "10.1.10.2", "192.168.1.2", nic, null, "1000", false); + final LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(arrayLbs, "64.10.2.10", "10.1.10.2", "192.168.1.2", nic, null, "1000", false, 50000L); cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, "10.1.10.2"); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); return cmd; @@ -799,7 +795,7 @@ protected LoadBalancerConfigCommand generateLoadBalancerConfigCommand2() { lbs.toArray(arrayLbs); final NicTO nic = new NicTO(); nic.setIp("10.1.10.2"); - final LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(arrayLbs, "64.10.2.10", "10.1.10.2", "192.168.1.2", nic, Long.valueOf(1), "1000", false); + final LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(arrayLbs, "64.10.2.10", "10.1.10.2", "192.168.1.2", nic, Long.valueOf(1), "1000", false, 50000L); cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, "10.1.10.2"); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); return cmd; diff --git a/core/src/test/java/com/cloud/network/HAProxyConfiguratorTest.java b/core/src/test/java/com/cloud/network/HAProxyConfiguratorTest.java index 72361c2880ed..073f976719b5 100644 --- a/core/src/test/java/com/cloud/network/HAProxyConfiguratorTest.java +++ b/core/src/test/java/com/cloud/network/HAProxyConfiguratorTest.java @@ -79,13 +79,14 @@ public void testGenerateConfigurationLoadBalancerConfigCommand() { 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); + LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "12", false, 0L); String result = genConfig(hpg, cmd); 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); + cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "4", true, 0L); result = genConfig(hpg, cmd); 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 @@ -93,6 +94,27 @@ public void testGenerateConfigurationLoadBalancerConfigCommand() { // httpmode } + /** + * Test method for {@link com.cloud.network.HAProxyConfigurator#generateConfiguration(com.cloud.agent.api.routing.LoadBalancerConfigCommand)}. + */ + @Test + public void testGenerateConfigurationLoadBalancerIdleTimeoutConfigCommand() { + LoadBalancerTO lb = new LoadBalancerTO("1", "10.2.0.1", 80, "http", "bla", false, false, false, null); + 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, "4", true, 0L); + String result = genConfig(hpg, cmd); + assertTrue("idleTimeout of 0 should not generate 'timeout server' in the resulting haproxy config", !result.contains("\ttimeout server")); + assertTrue("idleTimeout of 0 should not generate 'timeout client' in the resulting haproxy config", !result.contains("\ttimeout client")); + + cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "4", true, 1234L); + result = genConfig(hpg, cmd); + assertTrue("idleTimeout of 1234 should result in 'timeout server 1234' in the resulting haproxy config", result.contains("\ttimeout server 1234")); + assertTrue("idleTimeout of 1234 should result in 'timeout client 1234' in the resulting haproxy config", result.contains("\ttimeout client 1234")); + } + /** * Test method for {@link com.cloud.network.HAProxyConfigurator#generateConfiguration(com.cloud.agent.api.routing.LoadBalancerConfigCommand)}. */ @@ -106,7 +128,7 @@ public void testGenerateConfigurationLoadBalancerProxyProtocolConfigCommand() { 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); + LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "12", false, 0L); String result = genConfig(hpg, cmd); assertTrue("'send-proxy' should result if protocol is 'tcp-proxy'", result.contains("send-proxy")); } @@ -118,7 +140,7 @@ public void generateConfigurationTestWithCidrList() { 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); + LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "12", false, 0L); String result = genConfig(hpg, cmd); 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")); } @@ -131,7 +153,7 @@ public void generateConfigurationTestWithSslCert() { 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); + LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "12", false, 0L); 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")); } diff --git a/debian/changelog b/debian/changelog index cb53b473fc22..41f97748a0b5 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,20 @@ -cloudstack (4.22.0.0-SNAPSHOT) unstable; urgency=low +cloudstack (4.23.0.0-SNAPSHOT) unstable; urgency=low - * Update the version to 4.22.0.0-SNAPSHOT + * Update the version to 4.23.0.0-SNAPSHOT - -- the Apache CloudStack project Thu, Aug 28 11:58:36 2025 +0530 + -- the Apache CloudStack project Fri, 22 May 2026 10:20:00 -0300 + +cloudstack (4.22.1.0) unstable; urgency=low + + * Update the version to 4.22.1.0 + + -- the Apache CloudStack project Mon, 11 May 2026 20:26:07 +0530 + +cloudstack (4.22.0.0) unstable; urgency=low + + * Update the version to 4.22.0.0 + + -- the Apache CloudStack project Thu, 30 Oct 2025 19:23:55 +0530 cloudstack (4.21.0.0) unstable; urgency=low @@ -208,7 +220,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-agent.install b/debian/cloudstack-agent.install index 58715e0746ba..0b9e874cb423 100644 --- a/debian/cloudstack-agent.install +++ b/debian/cloudstack-agent.install @@ -16,6 +16,7 @@ # under the License. /etc/cloudstack/agent/agent.properties +/etc/cloudstack/agent/uefi.properties /etc/cloudstack/agent/environment.properties /etc/cloudstack/agent/log4j-cloud.xml /etc/default/cloudstack-agent diff --git a/debian/cloudstack-agent.postinst b/debian/cloudstack-agent.postinst index 758af6e068ff..cd070c2f7853 100755 --- a/debian/cloudstack-agent.postinst +++ b/debian/cloudstack-agent.postinst @@ -23,7 +23,7 @@ case "$1" in configure) OLDCONFDIR="/etc/cloud/agent" NEWCONFDIR="/etc/cloudstack/agent" - CONFFILES="agent.properties log4j.xml log4j-cloud.xml" + CONFFILES="agent.properties uefi.properties log4j.xml log4j-cloud.xml" mkdir -m 0755 -p /usr/share/cloudstack-agent/tmp diff --git a/debian/cloudstack-common.postinst b/debian/cloudstack-common.postinst index aa99edaee064..b11e6a3fe502 100644 --- a/debian/cloudstack-common.postinst +++ b/debian/cloudstack-common.postinst @@ -19,14 +19,14 @@ set -e CLOUDUTILS_DIR="/usr/share/pyshared/" -DIST_DIR=$(python3 -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(1))") -if which pycompile >/dev/null 2>&1; then - pycompile -p cloudstack-common -fi +# distutils was removed in Python 3.12 (Ubuntu 24.04); the Debian/Ubuntu-patched +# sysconfig 'deb_system' scheme gives the same /usr/lib/python3/dist-packages path. +DIST_DIR=$(python3 -c "import sysconfig; print(sysconfig.get_path('platlib', 'deb_system'))") -if which pycompile >/dev/null 2>&1; then - pycompile -p cloudstack-common /usr/share/cloudstack-common +if command -v py3compile >/dev/null 2>&1; then + py3compile -p cloudstack-common 2>/dev/null || echo "Warning: py3compile failed for cloudstack-common" >&2 + py3compile -p cloudstack-common /usr/share/cloudstack-common 2>/dev/null || echo "Warning: py3compile failed for cloudstack-common (/usr/share/cloudstack-common)" >&2 fi cp $CLOUDUTILS_DIR/cloud_utils.py $DIST_DIR diff --git a/debian/cloudstack-management.install b/debian/cloudstack-management.install index b2a32bd93c14..befc7049c30e 100644 --- a/debian/cloudstack-management.install +++ b/debian/cloudstack-management.install @@ -24,6 +24,7 @@ /etc/cloudstack/management/config.json /etc/cloudstack/extensions/Proxmox/proxmox.sh /etc/cloudstack/extensions/HyperV/hyperv.py +/etc/cloudstack/extensions/MaaS/maas.py /etc/default/cloudstack-management /etc/security/limits.d/cloudstack-limits.conf /etc/sudoers.d/cloudstack diff --git a/debian/control b/debian/control index 1292639ef304..cdf663ef8906 100644 --- a/debian/control +++ b/debian/control @@ -4,7 +4,7 @@ Priority: extra Maintainer: The Apache CloudStack Team Build-Depends: debhelper (>= 9), openjdk-17-jdk | java17-sdk | java17-jdk | zulu-17 | openjdk-11-jdk | java11-sdk | java11-jdk | zulu-11, genisoimage, python-mysql.connector | python3-mysql.connector | mysql-connector-python-py3, maven (>= 3) | maven3, - python (>= 2.7) | python2 (>= 2.7), python3 (>= 3), python-setuptools, python3-setuptools, + python3 (>= 3), python3-setuptools, nodejs (>= 12), lsb-release, dh-systemd | debhelper (>= 13) Standards-Version: 3.8.1 Homepage: http://www.cloudstack.org/ @@ -24,7 +24,7 @@ Description: CloudStack server library Package: cloudstack-agent Architecture: all -Depends: ${python:Depends}, ${python3:Depends}, openjdk-17-jre-headless | java17-runtime-headless | java17-runtime | zulu-17, cloudstack-common (= ${source:Version}), lsb-base (>= 9), openssh-client, qemu-kvm (>= 2.5) | qemu-system-x86 (>= 5.2), libvirt-bin (>= 1.3) | libvirt-daemon-system (>= 3.0), iproute2, ebtables, vlan, ipset, python3-libvirt, ethtool, iptables, cryptsetup, rng-tools, rsync, lsb-release, ufw, apparmor, cpu-checker, libvirt-daemon-driver-storage-rbd, sysstat +Depends: ${python:Depends}, ${python3:Depends}, openjdk-17-jre-headless | java17-runtime-headless | java17-runtime | zulu-17, cloudstack-common (= ${source:Version}), lsb-base (>= 9), openssh-client, qemu-kvm (>= 2.5) | qemu-system-x86 (>= 5.2), libvirt-bin (>= 1.3) | libvirt-daemon-system (>= 3.0), iproute2, ebtables, vlan, ipset, python3-libvirt, ethtool, iptables, cryptsetup, rng-tools, rsync, ovmf, swtpm, lsb-release, ufw, apparmor, cpu-checker, libvirt-daemon-driver-storage-rbd, sysstat, python3-libnbd, socat Recommends: init-system-helpers Conflicts: cloud-agent, cloud-agent-libs, cloud-agent-deps, cloud-agent-scripts Description: CloudStack agent diff --git a/debian/rules b/debian/rules index 4c3023aeae2f..327447823308 100755 --- a/debian/rules +++ b/debian/rules @@ -4,7 +4,6 @@ VERSION := $(shell grep '' pom.xml | head -2 | tail -1 | cut -d'>' -f2 PACKAGE = $(shell dh_listpackages|head -n 1|cut -d '-' -f 1) SYSCONFDIR = "/etc" DESTDIR = "debian/tmp" -CMK_REL := $(shell wget -O - "https://api.github.com/repos/apache/cloudstack-cloudmonkey/releases" 2>/dev/null | jq -r '.[0].tag_name') %: dh $@ --with systemd @@ -90,12 +89,13 @@ override_dh_auto_install: 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 + wget https://github.com/apache/cloudstack-cloudmonkey/releases/latest/download/cmk.linux.x86-64 -O $(DESTDIR)/usr/bin/cmk chmod +x $(DESTDIR)/usr/bin/cmk # nast hack for a couple of configuration files mv $(DESTDIR)/$(SYSCONFDIR)/$(PACKAGE)/server/cloudstack-limits.conf $(DESTDIR)/$(SYSCONFDIR)/security/limits.d/ mv $(DESTDIR)/$(SYSCONFDIR)/$(PACKAGE)/server/cloudstack-sudoers $(DESTDIR)/$(SYSCONFDIR)/sudoers.d/$(PACKAGE) + sed -i '/requiretty/d' $(DESTDIR)/$(SYSCONFDIR)/sudoers.d/$(PACKAGE) chmod 0440 $(DESTDIR)/$(SYSCONFDIR)/sudoers.d/$(PACKAGE) install -D client/target/utilities/bin/cloud-update-xenserver-licenses $(DESTDIR)/usr/bin/cloudstack-update-xenserver-licenses diff --git a/developer/pom.xml b/developer/pom.xml index c11415e04fa2..0a0979ee0379 100644 --- a/developer/pom.xml +++ b/developer/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.22.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT @@ -66,7 +66,7 @@ org.codehaus.mojo properties-maven-plugin - 1.0-alpha-2 + 1.2.1 initialize diff --git a/engine/api/pom.xml b/engine/api/pom.xml index 9dcdb8e8f52c..cb50ef0cd46b 100644 --- a/engine/api/pom.xml +++ b/engine/api/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.22.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT ../pom.xml 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 c05c29add556..c7238a513693 100644 --- a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java +++ b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java @@ -59,6 +59,8 @@ */ public interface VirtualMachineManager extends Manager { + String KVM_BLANK_VM_TEMPLATE_NAME = "kvm-blank-vm-template"; + ConfigKey ExecuteInSequence = new ConfigKey<>("Advanced", Boolean.class, "execute.in.sequence.hypervisor.commands", "false", "If set to true, start, stop, reboot, copy and migrate commands will be serialized on the agent side. If set to false the commands are executed in parallel. Default value is false.", false); @@ -106,9 +108,14 @@ public interface VirtualMachineManager extends Manager { ConfigKey VmSyncPowerStateTransitioning = new ConfigKey<>("Advanced", Boolean.class, "vm.sync.power.state.transitioning", "true", "Whether to sync power states of the transitioning and stalled VMs while processing VM power reports.", false); + ConfigKey SystemVmEnableUserData = new ConfigKey<>(Boolean.class, "systemvm.userdata.enabled", "Advanced", "false", + "Enable user data for system VMs. When enabled, the CPVM, SSVM, and Router system VMs will use the values from the global settings console.proxy.vm.userdata, secstorage.vm.userdata, and virtual.router.userdata, respectively, to provide cloud-init user data to the VM.", + true, ConfigKey.Scope.Zone, null); interface Topics { String VM_POWER_STATE = "vm.powerstate"; + String VM_LIFECYCLE_STATE = "vm.lifecycle.state"; + String VM_ACTION = "vm.action"; } /** @@ -178,15 +185,6 @@ void orchestrateStart(String vmUuid, Map pa void advanceReboot(String vmUuid, Map params) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, OperationTimedoutException; - /** - * Check to see if a virtual machine can be upgraded to the given service offering - * - * @param vm - * @param offering - * @return true if the host can handle the upgrade, false otherwise - */ - boolean isVirtualMachineUpgradable(final VirtualMachine vm, final ServiceOffering offering); - VirtualMachine findById(long vmId); void storageMigration(String vmUuid, Map volumeToPool); @@ -227,6 +225,8 @@ NicProfile addVmToNetwork(VirtualMachine vm, Network network, NicProfile request Boolean updateDefaultNicForVM(VirtualMachine vm, Nic nic, Nic defaultNic); + boolean updateVmNic(VirtualMachine vm, Nic nic, Boolean enabled) throws ResourceUnavailableException; + /** * @param vm * @param network @@ -274,7 +274,7 @@ static String getHypervisorHostname(String name) { * - Remove the references of the VM and its volumes, nics, IPs from database * - Keep the VM as it is on the hypervisor */ - boolean unmanage(String vmUuid); + Pair unmanage(String vmUuid, Long paramHostId); UserVm restoreVirtualMachine(long vmId, Long newTemplateId, Long rootDiskOfferingId, boolean expunge, Map details) throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException; @@ -316,4 +316,8 @@ void checkDeploymentPlan(VirtualMachine virtualMachine, VirtualMachineTemplate t ServiceOffering serviceOffering, Account systemAccount, DeploymentPlan plan) throws InsufficientServerCapacityException; + boolean isBlankInstanceDefaultTemplate(VirtualMachineTemplate template); + + boolean isBlankInstance(VirtualMachineTemplate template); + } 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 adce5f2f8b43..109a44488ec4 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 @@ -76,44 +76,67 @@ public interface NetworkOrchestrationService { */ Long RVRHandoverTime = 10000L; - ConfigKey MinVRVersion = new ConfigKey(String.class, MinVRVersionCK, "Advanced", "4.10.0", + ConfigKey MinVRVersion = new ConfigKey<>(String.class, MinVRVersionCK, "Advanced", "4.10.0", "What version should the Virtual Routers report", true, ConfigKey.Scope.Zone, null); - ConfigKey NetworkLockTimeout = new ConfigKey(Integer.class, NetworkLockTimeoutCK, "Network", "600", - "Lock wait timeout (seconds) while implementing network", true, Scope.Global, null); + ConfigKey NetworkLockTimeout = new ConfigKey<>(Integer.class, NetworkLockTimeoutCK, "Network", "600", + "Lock wait timeout (seconds) while implementing Network", true, Scope.Global, null); - ConfigKey DeniedRoutes = new ConfigKey(String.class, "denied.routes", "Network", "", + ConfigKey DeniedRoutes = new ConfigKey<>(String.class, "denied.routes", "Network", "", "Routes that are denied, can not be used for Static Routes creation for the VPC Private Gateway", true, ConfigKey.Scope.Zone, null); - ConfigKey GuestDomainSuffix = new ConfigKey(String.class, GuestDomainSuffixCK, "Network", "cloud.internal", - "Default domain name for vms inside virtualized networks fronted by router", true, ConfigKey.Scope.Zone, null); + ConfigKey GuestDomainSuffix = new ConfigKey<>(String.class, GuestDomainSuffixCK, "Network", "cloud.internal", + "Default domain name for vms inside virtualized networks fronted by router", true, ConfigKey.Scope.Zone, null); - ConfigKey NetworkThrottlingRate = new ConfigKey("Network", Integer.class, NetworkThrottlingRateCK, "200", - "Default data transfer rate in megabits per second allowed in network.", true, ConfigKey.Scope.Zone); + ConfigKey NetworkThrottlingRate = new ConfigKey<>("Network", Integer.class, NetworkThrottlingRateCK, "200", + "Default data transfer rate in megabits per second allowed in network.", true, ConfigKey.Scope.Zone); - ConfigKey PromiscuousMode = new ConfigKey("Advanced", Boolean.class, "network.promiscuous.mode", "false", - "Whether to allow or deny promiscuous mode on nics for applicable network elements such as for vswitch/dvswitch portgroups.", true); + ConfigKey DhcpLeaseTimeout = new ConfigKey<>("Network", Integer.class, "dhcp.lease.timeout", "0", + "DHCP lease time in seconds for VMs. Use 0 for infinite lease time (default). A non-zero value sets the lease duration in seconds.", + true, ConfigKey.Scope.Zone); - ConfigKey MacAddressChanges = new ConfigKey("Advanced", Boolean.class, "network.mac.address.changes", "true", - "Whether to allow or deny mac address changes on nics for applicable network elements such as for vswitch/dvswitch porgroups.", true); + ConfigKey PromiscuousMode = new ConfigKey<>("Advanced", Boolean.class, "network.promiscuous.mode", "false", + "Whether to allow or deny promiscuous mode on NICs for applicable network elements such as for vswitch/dvswitch portgroups.", true); - ConfigKey ForgedTransmits = new ConfigKey("Advanced", Boolean.class, "network.forged.transmits", "true", - "Whether to allow or deny forged transmits on nics for applicable network elements such as for vswitch/dvswitch portgroups.", true); + ConfigKey MacAddressChanges = new ConfigKey<>("Advanced", Boolean.class, "network.mac.address.changes", "true", + "Whether to allow or deny mac address changes on NICs for applicable network elements such as for vswitch/dvswitch porgroups.", true); - ConfigKey MacLearning = new ConfigKey("Advanced", Boolean.class, "network.mac.learning", "false", + ConfigKey ForgedTransmits = new ConfigKey<>("Advanced", Boolean.class, "network.forged.transmits", "true", + "Whether to allow or deny forged transmits on NICs for applicable network elements such as for vswitch/dvswitch portgroups.", true); + + ConfigKey MacLearning = new ConfigKey<>("Advanced", Boolean.class, "network.mac.learning", "false", "Whether to allow or deny MAC learning on nics for applicable network elements such as for dvswitch portgroups.", true); - ConfigKey RollingRestartEnabled = new ConfigKey("Advanced", Boolean.class, "network.rolling.restart", "true", + ConfigKey RollingRestartEnabled = new ConfigKey<>("Advanced", Boolean.class, "network.rolling.restart", "true", "Whether to allow or deny rolling restart of network routers.", true); - static final ConfigKey TUNGSTEN_ENABLED = new ConfigKey<>(Boolean.class, "tungsten.plugin.enable", "Advanced", "false", + ConfigKey TUNGSTEN_ENABLED = new ConfigKey<>(Boolean.class, "tungsten.plugin.enable", "Advanced", "false", "Indicates whether to enable the Tungsten plugin", false, ConfigKey.Scope.Zone, null); - static final ConfigKey NSX_ENABLED = new ConfigKey<>(Boolean.class, "nsx.plugin.enable", "Advanced", "false", + 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); + ConfigKey NETWORK_LB_HAPROXY_MAX_CONN = new ConfigKey<>( + "Network", + Integer.class, + "network.loadbalancer.haproxy.max.conn", + "4096", + "Load Balancer(haproxy) maximum number of concurrent connections(global max)", + true, + Scope.Global); + ConfigKey NETWORK_LB_HAPROXY_IDLE_TIMEOUT = new ConfigKey<>( + "Network", + Long.class, + "network.loadbalancer.haproxy.idle.timeout", + "50000", + "Load Balancer(haproxy) idle timeout in milliseconds. Use 0 for infinite.", + true, + Scope.Global); + + ConfigKey VmNetworkThrottlingRate = new ConfigKey("Network", Integer.class, "vm.network.throttling.rate", "200", + "Default data transfer rate in megabits per second allowed in User vm's default network.", true, ConfigKey.Scope.Zone); List setupNetwork(Account owner, NetworkOffering offering, DeploymentPlan plan, String name, String displayText, boolean isDefault) throws ConcurrentOperationException; @@ -122,6 +145,8 @@ List setupNetwork(Account owner, NetworkOffering offering, Ne boolean errorIfAlreadySetup, Long domainId, ACLType aclType, Boolean subdomainAccess, Long vpcId, Boolean isDisplayNetworkEnabled) throws ConcurrentOperationException; + boolean isIsolationMethodNetworkExtension(Long networkOfferingId); + void allocate(VirtualMachineProfile vm, LinkedHashMap> networks, Map> extraDhcpOptions) throws InsufficientCapacityException, ConcurrentOperationException; @@ -129,7 +154,7 @@ void allocate(VirtualMachineProfile vm, LinkedHashMap extraDhcpOptions); @@ -158,16 +183,15 @@ void prepare(VirtualMachineProfile profile, DeployDestination dest, ReservationC Pair implementNetwork(long networkId, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException; - Map getExtraDhcpOptions(long nicId); - /** * Returns all extra dhcp options which are set on the provided nic * @param nicId * @return map which maps the dhcp value on it's option code */ + Map getExtraDhcpOptions(long nicId); + /** * prepares vm nic change for migration - * * This method will be called in migration transaction before the vm migration. * @param vm * @param dest @@ -176,7 +200,6 @@ void prepare(VirtualMachineProfile profile, DeployDestination dest, ReservationC /** * commit vm nic change for migration - * * This method will be called in migration transaction after the successful * vm migration. * @param src @@ -186,7 +209,6 @@ void prepare(VirtualMachineProfile profile, DeployDestination dest, ReservationC /** * rollback vm nic change for migration - * * This method will be called in migaration transaction after vm migration * failure. * @param src @@ -207,6 +229,11 @@ Network createGuestNetwork(long networkOfferingId, String name, String displayTe Boolean displayNetworkEnabled, String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId, String routerIp, String routerIpv6, String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, Pair vrIfaceMTUs, Integer networkCidrSize) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException; + Network createGuestNetwork(long networkOfferingId, String name, String displayText, String gateway, String cidr, String vlanId, boolean bypassVlanOverlapCheck, String networkDomain, Account owner, + Long domainId, PhysicalNetwork physicalNetwork, long zoneId, ACLType aclType, Boolean subdomainAccess, Long vpcId, String ip6Gateway, String ip6Cidr, + Boolean displayNetworkEnabled, String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId, String routerIp, String routerIpv6, + String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, Pair vrIfaceMTUs, Integer networkCidrSize, boolean keepMacAddressOnPublicNic) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException; + UserDataServiceProvider getPasswordResetProvider(Network network); UserDataServiceProvider getSSHKeyResetProvider(Network network); @@ -266,7 +293,7 @@ NicProfile prepareNic(VirtualMachineProfile vmProfile, DeployDestination dest, R void releaseNic(VirtualMachineProfile vmProfile, Nic nic) throws ConcurrentOperationException, ResourceUnavailableException; NicProfile createNicForVm(Network network, NicProfile requested, ReservationContext context, VirtualMachineProfile vmProfile, boolean prepare) - throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, ConcurrentOperationException, InsufficientCapacityException, + throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException; NetworkProfile convertNetworkToNetworkProfile(long networkId); @@ -277,7 +304,7 @@ boolean restartNetwork(Long networkId, Account callerAccount, User callerUser, b boolean shutdownNetworkElementsAndResources(ReservationContext context, boolean b, Network network); void implementNetworkElementsAndResources(DeployDestination dest, ReservationContext context, Network network, NetworkOffering findById) - throws ConcurrentOperationException, InsufficientAddressCapacityException, ResourceUnavailableException, InsufficientCapacityException; + throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException; Map finalizeServicesAndProvidersForNetwork(NetworkOffering offering, Long physicalNetworkId); @@ -305,7 +332,7 @@ void implementNetworkElementsAndResources(DeployDestination dest, ReservationCon void removeDhcpServiceInSubnet(Nic nic); - boolean resourceCountNeedsUpdate(NetworkOffering ntwkOff, ACLType aclType); + boolean isResourceCountUpdateNeeded(NetworkOffering networkOffering); void prepareAllNicsForMigration(VirtualMachineProfile vm, DeployDestination dest); diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/StorageOrchestrationService.java b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/StorageOrchestrationService.java index 481d0ebbc769..4af0c806060b 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/StorageOrchestrationService.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/StorageOrchestrationService.java @@ -18,12 +18,17 @@ package org.apache.cloudstack.engine.orchestration.service; import java.util.List; +import java.util.concurrent.Future; import org.apache.cloudstack.api.response.MigrationResponse; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService.TemplateApiResult; import org.apache.cloudstack.storage.ImageStoreService.MigrationPolicy; public interface StorageOrchestrationService { MigrationResponse migrateData(Long srcDataStoreId, List destDatastores, MigrationPolicy migrationPolicy); MigrationResponse migrateResources(Long srcImgStoreId, Long destImgStoreId, List templateIdList, List snapshotIdList); + + Future orchestrateTemplateCopyFromSecondaryStores(long templateId, DataStore destStore); } 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 ccb5bba1c0a4..141596407fe8 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.Set; import com.cloud.exception.ResourceAllocationException; +import com.cloud.storage.Storage; import com.cloud.utils.Pair; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; @@ -89,11 +90,11 @@ public interface VolumeOrchestrationService { "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'.", + "Order in which storage pool within a cluster will be considered for volume allocation. The value can be 'random', 'firstfit', 'userdispersing', or 'firstfitleastconsumed'.", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.Select, - "random,firstfit,userdispersing,userconcentratedpod_random,userconcentratedpod_firstfit,firstfitleastconsumed"); + "random,firstfit,userdispersing,firstfitleastconsumed"); VolumeInfo moveVolume(VolumeInfo volume, long destPoolDcId, Long destPoolPodId, Long destPoolClusterId, HypervisorType dataDiskHyperType) throws ConcurrentOperationException, StorageUnavailableException; @@ -119,9 +120,9 @@ VolumeInfo moveVolume(VolumeInfo volume, long destPoolDcId, Long destPoolPodId, void destroyVolume(Volume volume); DiskProfile allocateRawVolume(Type type, String name, DiskOffering offering, Long size, Long minIops, Long maxIops, VirtualMachine vm, VirtualMachineTemplate template, - Account owner, Long deviceId); + Account owner, Long deviceId, Long kmsKeyId, boolean incrementResourceCount); - VolumeInfo createVolumeOnPrimaryStorage(VirtualMachine vm, VolumeInfo volume, HypervisorType rootDiskHyperType, StoragePool storagePool) throws NoTransitionException; + VolumeInfo createVolumeOnPrimaryStorage(VirtualMachine vm, VolumeInfo volume, HypervisorType rootDiskHyperType, StoragePool storagePool, Long clusterId, Long podId) throws NoTransitionException; void release(VirtualMachineProfile profile); @@ -149,7 +150,7 @@ DiskProfile allocateRawVolume(Type type, String name, DiskOffering offering, Lon * 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, Volume volume, Snapshot snapshot); + Account owner, Long kmsKeyId, Volume volume, Snapshot snapshot); String getVmNameFromVolumeId(long volumeId); @@ -182,10 +183,10 @@ List allocateTemplatedVolumes(Type type, String name, DiskOffering */ DiskProfile importVolume(Type type, String name, DiskOffering offering, Long sizeInBytes, Long minIops, Long maxIops, Long zoneId, HypervisorType hypervisorType, VirtualMachine vm, VirtualMachineTemplate template, - Account owner, Long deviceId, Long poolId, String path, String chainInfo); + Account owner, Long deviceId, Long poolId, Storage.StoragePoolType poolType, String path, String chainInfo); DiskProfile updateImportedVolume(Type type, DiskOffering offering, VirtualMachine vm, VirtualMachineTemplate template, - Long deviceId, Long poolId, String path, String chainInfo, DiskProfile diskProfile); + Long deviceId, Long poolId, Storage.StoragePoolType poolType, String path, String chainInfo, DiskProfile diskProfile); /** * Unmanage VM volumes 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 6be71b3cb250..887aeaef0736 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 @@ -71,7 +71,7 @@ VirtualMachineEntity createVirtualMachine(@QueryParam("id") String id, @QueryPar @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; + @QueryParam("root-disk-offering-id") Long rootDiskOfferingId, @QueryParam("root-disk-kms-key-id") Long rootDiskKmsKeyId, List dataDiskInfoList, Volume volume, Snapshot snapshot) throws InsufficientCapacityException; @POST VirtualMachineEntity createVirtualMachineFromScratch(@QueryParam("id") String id, @QueryParam("owner") String owner, @QueryParam("iso-id") String isoId, @@ -80,7 +80,7 @@ VirtualMachineEntity createVirtualMachineFromScratch(@QueryParam("id") String id @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; + @QueryParam("root-disk-kms-key-id") Long rootDiskKmsKeyId, @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/ClusterScope.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/ClusterScope.java index b0ed7d7d52b5..68c7ed40d8f4 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/ClusterScope.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/ClusterScope.java @@ -19,6 +19,7 @@ package org.apache.cloudstack.engine.subsystem.api.storage; import com.cloud.storage.ScopeType; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; public class ClusterScope extends AbstractScope { private ScopeType type = ScopeType.CLUSTER; @@ -51,4 +52,9 @@ public Long getZoneId() { return this.zoneId; } + @Override + public String toString() { + return String.format("ClusterScope %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "zoneId", "clusterId", "podId")); + } } diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreProvider.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreProvider.java index 41c1d9407454..7ff206db3c99 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreProvider.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreProvider.java @@ -29,6 +29,11 @@ public interface DataStoreProvider { String SAMPLE_IMAGE = "Sample"; String SMB = "NFS"; String DEFAULT_PRIMARY = "DefaultPrimary"; + /** + * Primary storage provider name for NetApp ONTAP ({@code OntapPrimaryDatastoreProvider#getName}). + * Single canonical value; use this instead of duplicating the string across modules. + */ + String ONTAP_PLUGIN_NAME = "NetApp ONTAP"; enum DataStoreProviderType { PRIMARY, IMAGE, ImageCache, OBJECT diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/HostScope.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/HostScope.java index 6e0bc618bfe2..7e6ffe7d4f74 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/HostScope.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/HostScope.java @@ -19,8 +19,10 @@ package org.apache.cloudstack.engine.subsystem.api.storage; import com.cloud.storage.ScopeType; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; public class HostScope extends AbstractScope { + private ScopeType type = ScopeType.HOST; private Long hostId; private Long clusterId; private Long zoneId; @@ -34,7 +36,7 @@ public HostScope(Long hostId, Long clusterId, Long zoneId) { @Override public ScopeType getScopeType() { - return ScopeType.HOST; + return this.type; } @Override @@ -49,4 +51,10 @@ public Long getClusterId() { public Long getZoneId() { return zoneId; } + + @Override + public String toString() { + return String.format("HostScope %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "zoneId", "clusterId", "hostId")); + } } 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 ce9f4a67a732..afa5d4e36db8 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,11 +32,12 @@ enum State { Migrated("The object has been migrated"), Destroying("Template is destroying"), Destroyed("Template is destroyed"), - Failed("Failed to download template"), + Failed("Failed to download Template"), Hidden("The object is hidden from the user"); - String _description; - private State(String description) { + final String _description; + + State(String description) { _description = description; } @@ -49,7 +50,7 @@ enum Event { CreateRequested, CreateOnlyRequested, DestroyRequested, - OperationSuccessed, + OperationSucceeded, OperationFailed, CopyRequested, CopyingRequested, diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreLifeCycle.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreLifeCycle.java index 54f3c63f8d73..1acaccf09df4 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreLifeCycle.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreLifeCycle.java @@ -24,8 +24,8 @@ import com.cloud.storage.StoragePool; public interface PrimaryDataStoreLifeCycle extends DataStoreLifeCycle { - public static final String CAPACITY_BYTES = "capacityBytes"; - public static final String CAPACITY_IOPS = "capacityIops"; + String CAPACITY_BYTES = "capacityBytes"; + String CAPACITY_IOPS = "capacityIops"; void updateStoragePool(StoragePool storagePool, Map details); void enableStoragePool(DataStore store); diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotDataFactory.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotDataFactory.java index 2a2db4af3e53..32434829c074 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotDataFactory.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotDataFactory.java @@ -21,6 +21,7 @@ import java.util.List; import com.cloud.storage.DataStoreRole; +import com.cloud.utils.fsm.NoTransitionException; public interface SnapshotDataFactory { SnapshotInfo getSnapshot(long snapshotId, DataStore store); @@ -29,6 +30,8 @@ public interface SnapshotDataFactory { SnapshotInfo getSnapshot(long snapshotId, long storeId, DataStoreRole role); + SnapshotInfo getSnapshotIncludingRemoved(long snapshotId, long storeId, DataStoreRole role); + SnapshotInfo getSnapshotWithRoleAndZone(long snapshotId, DataStoreRole role, long zoneId); SnapshotInfo getSnapshotOnPrimaryStore(long snapshotId); @@ -42,4 +45,6 @@ public interface SnapshotDataFactory { List listSnapshotOnCache(long snapshotId); SnapshotInfo getReadySnapshotOnCache(long snapshotId); + + void updateOperationFailed(long snapshotId) throws NoTransitionException; } diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotInfo.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotInfo.java index d47aa5865a0f..997a66ec5901 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotInfo.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotInfo.java @@ -25,7 +25,7 @@ public interface SnapshotInfo extends DataObject, Snapshot { ConfigKey BackupSnapshotAfterTakingSnapshot = new ConfigKey<>(Boolean.class, "snapshot.backup.to.secondary", "Snapshots", "true", "Indicates whether to always" - + " backup primary storage snapshot to secondary storage. Keeping snapshots only on Primary storage is applicable for KVM + Ceph only.", false, ConfigKey.Scope.Global, + + " backup primary storage Snapshot to secondary storage. Keeping Snapshots only on Primary storage is applicable for KVM + Ceph only.", false, ConfigKey.Scope.Global, null); SnapshotInfo getParent(); diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/TemplateService.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/TemplateService.java index 115cf024617f..2f8d57171bc9 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/TemplateService.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/TemplateService.java @@ -67,6 +67,12 @@ public TemplateInfo getTemplate() { void handleTemplateSync(DataStore store); + void enforceSecStorageCopyLimit(long templateId, long zoneId); + + boolean canCopyTemplateToImageStore(long templateId, long zoneId); + + void replicateTemplateUpToCap(long templateId, long zoneId); + void downloadBootstrapSysTemplate(DataStore store); void addSystemVMTemplatesToSecondary(DataStore store); @@ -78,4 +84,8 @@ public TemplateInfo getTemplate() { AsyncCallFuture createDatadiskTemplateAsync(TemplateInfo parentTemplate, TemplateInfo dataDiskTemplate, String path, String diskId, long fileSize, boolean bootable); List getTemplateDatadisksOnImageStore(TemplateInfo templateInfo, String configurationId); -} + + AsyncCallFuture copyTemplateToImageStore(DataObject source, DataStore destStore); + + void handleTemplateCopyFromSecondaryStores(long templateId, DataStore destStore); + } diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VMSnapshotStrategy.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VMSnapshotStrategy.java index 6372aa3bd949..2939342ec27c 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VMSnapshotStrategy.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VMSnapshotStrategy.java @@ -18,6 +18,7 @@ */ package org.apache.cloudstack.engine.subsystem.api.storage; +import com.cloud.utils.fsm.NoTransitionException; import com.cloud.vm.snapshot.VMSnapshot; public interface VMSnapshotStrategy { @@ -44,4 +45,6 @@ public interface VMSnapshotStrategy { * @return true if vm snapshot removed from DB, false if not. */ boolean deleteVMSnapshotFromDB(VMSnapshot vmSnapshot, boolean unmanage); + + void updateOperationFailed(VMSnapshot vmSnapshot) throws NoTransitionException; } diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java index 8b0171870765..4937edd33d1f 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java @@ -103,4 +103,21 @@ public interface VolumeInfo extends DownloadableDataInfo, Volume { List getCheckpointPaths(); Set getCheckpointImageStoreUrls(); + + /** + * Gets the destination host ID hint for CLVM volume creation. + * This is used to route volume creation commands to the specific host where the VM will be deployed. + * Only applicable for CLVM storage pools to avoid shared mode activation. + * + * @return The host ID where the volume should be created, or null if not set + */ + Long getDestinationHostId(); + + /** + * Sets the destination host ID hint for CLVM volume creation. + * This should be set before volume creation when the destination host is known. + * + * @param hostId The host ID where the volume should be created + */ + void setDestinationHostId(Long hostId); } diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java index 682473ec94fc..a7d82d0b9628 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java @@ -30,6 +30,7 @@ import com.cloud.host.Host; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.offering.DiskOffering; +import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.Volume; import com.cloud.user.Account; import com.cloud.utils.Pair; @@ -123,4 +124,71 @@ boolean copyPoliciesBetweenVolumesAndDestroySourceVolumeAfterMigration(ObjectInD void checkAndRepairVolumeBasedOnConfig(DataObject dataObject, Host host); void validateChangeDiskOfferingEncryptionType(long existingDiskOfferingId, long newDiskOfferingId); + + /** + * Transfers exclusive lock for a volume on cluster-based storage (e.g., CLVM/CLVM_NG) from one host to another. + * This is used for storage that requires host-level lock management for volumes on shared storage pools. + * For non-CLVM pool types, this method returns false without taking action. + * + * @param volume The volume to transfer lock for + * @param sourceHostId Host currently holding the exclusive lock + * @param destHostId Host to receive the exclusive lock + * @return true if lock transfer succeeded or was not needed, false if it failed + */ + boolean transferVolumeLock(VolumeInfo volume, Long sourceHostId, Long destHostId); + + /** + * Finds which host currently has the exclusive lock on a CLVM volume. + * Checks in order: explicit lock tracking, attached VM's host, or first available cluster host. + * + * @param volume The CLVM volume + * @return Host ID that has the exclusive lock, or null if cannot be determined + */ + Long findVolumeLockHost(VolumeInfo volume); + + /** + * Performs lightweight CLVM lock migration for a volume to a target host. + * This transfers the LVM exclusive lock without copying data (CLVM volumes are on shared cluster storage). + * If the volume already has the lock on the destination host, no action is taken. + * + * @param volume The volume to migrate lock for + * @param destHostId Destination host ID + * @return Updated VolumeInfo after lock migration + */ + VolumeInfo performLockMigration(VolumeInfo volume, Long destHostId); + + /** + * Checks if both storage pools are CLVM type (CLVM or CLVM_NG). + * + * @param volumePoolType Storage pool type for the volume + * @param vmPoolType Storage pool type for the VM + * @return true if both pools are CLVM type (CLVM or CLVM_NG) + */ + boolean areBothPoolsClvmType(StoragePoolType volumePoolType, StoragePoolType vmPoolType); + + /** + * Determines if CLVM lock transfer is required when a volume is already on the correct storage pool. + * + * @param volumeToAttach The volume being attached + * @param volumePoolType Storage pool type for the volume + * @param vmPoolType Storage pool type for the VM's existing volume + * @param volumePoolId Storage pool ID for the volume + * @param vmPoolId Storage pool ID for the VM's existing volume + * @param vmHostId VM's current host ID (or last host ID if stopped) + * @return true if CLVM lock transfer is needed + */ + boolean isLockTransferRequired(VolumeInfo volumeToAttach, StoragePoolType volumePoolType, StoragePoolType vmPoolType, + Long volumePoolId, Long vmPoolId, Long vmHostId); + + /** + * Determines if lightweight CLVM migration is needed instead of full data copy. + * + * @param volumePoolType Storage pool type for the volume + * @param vmPoolType Storage pool type for the VM + * @param volumePoolPath Storage pool path for the volume + * @param vmPoolPath Storage pool path for the VM + * @return true if lightweight migration should be used + */ + boolean isLightweightMigrationNeeded(StoragePoolType volumePoolType, StoragePoolType vmPoolType, + String volumePoolPath, String vmPoolPath); } diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/ZoneScope.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/ZoneScope.java index a0d75b5cc7c9..fa704d05b1ae 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/ZoneScope.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/ZoneScope.java @@ -19,6 +19,7 @@ package org.apache.cloudstack.engine.subsystem.api.storage; import com.cloud.storage.ScopeType; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; public class ZoneScope extends AbstractScope { private ScopeType type = ScopeType.ZONE; @@ -39,4 +40,9 @@ public Long getScopeId() { return this.zoneId; } + @Override + public String toString() { + return String.format("ZoneScope %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "zoneId")); + } } diff --git a/engine/components-api/pom.xml b/engine/components-api/pom.xml index ed67de2e0d88..49d41d36f83d 100644 --- a/engine/components-api/pom.xml +++ b/engine/components-api/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.22.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT ../pom.xml diff --git a/engine/components-api/src/main/java/com/cloud/agent/AgentManager.java b/engine/components-api/src/main/java/com/cloud/agent/AgentManager.java index 0aa5805b1601..4d63fae33560 100644 --- a/engine/components-api/src/main/java/com/cloud/agent/AgentManager.java +++ b/engine/components-api/src/main/java/com/cloud/agent/AgentManager.java @@ -54,6 +54,10 @@ public interface AgentManager { "This timeout overrides the wait global config. This holds a comma separated key value pairs containing timeout (in seconds) for specific commands. " + "For example: DhcpEntryCommand=600, SavePasswordCommand=300, VmDataCommand=300", false); + ConfigKey KVMHostDiscoverySshPort = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Integer.class, + "kvm.host.discovery.ssh.port", String.valueOf(Host.DEFAULT_SSH_PORT), "SSH port used for KVM host discovery and any other operations on host (using SSH)." + + " Please note that this is applicable when port is not defined through host url while adding the KVM host.", true, ConfigKey.Scope.Cluster); + enum TapAgentsAction { Add, Del, Contains, } @@ -172,4 +176,6 @@ enum TapAgentsAction { void propagateChangeToAgents(Map params); boolean transferDirectAgentsFromMS(String fromMsUuid, long fromMsId, long timeoutDurationInMs, boolean excludeHostsInMaintenance); + + int getHostSshPort(HostVO host); } diff --git a/engine/components-api/src/main/java/com/cloud/alert/AlertManager.java b/engine/components-api/src/main/java/com/cloud/alert/AlertManager.java index 3d4e6579f7ca..46993b066a49 100644 --- a/engine/components-api/src/main/java/com/cloud/alert/AlertManager.java +++ b/engine/components-api/src/main/java/com/cloud/alert/AlertManager.java @@ -49,10 +49,13 @@ public interface AlertManager extends Manager, AlertService { "Percentage (as a value between 0 and 1) of guest network IPv6 subnet utilization above which alerts will be sent.", true); + ConfigKey AllowedRepetitiveAlertTypes = new ConfigKey<>(ConfigKey.CATEGORY_ALERT, String.class, + "alert.allowed.repetitive.types", "", + "Comma-separated list of alert types (by name) that can be sent multiple times", true); + void clearAlert(AlertType alertType, long dataCenterId, long podId); void recalculateCapacity(); void sendAlert(AlertType alertType, long dataCenterId, Long podId, String subject, String body); - } diff --git a/engine/components-api/src/main/java/com/cloud/capacity/CapacityManager.java b/engine/components-api/src/main/java/com/cloud/capacity/CapacityManager.java index 4c81c7359f25..abaf6ea967d0 100644 --- a/engine/components-api/src/main/java/com/cloud/capacity/CapacityManager.java +++ b/engine/components-api/src/main/java/com/cloud/capacity/CapacityManager.java @@ -133,6 +133,20 @@ public interface CapacityManager { "capacity.calculate.workers", "1", "Number of worker threads to be used for capacities calculation", true); + ConfigKey KvmMemoryDynamicScalingCapacity = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, + Integer.class, "kvm.memory.dynamic.scaling.capacity", "0", + "Defines the maximum memory capacity in MiB for which VMs can be dynamically scaled to with KVM. " + + "The 'kvm.memory.dynamic.scaling.capacity' setting's value will be used to define the value of the " + + "'' element of domain XMLs. If it is set to a value less than or equal to '0', then the host's memory capacity will be considered.", + true, ConfigKey.Scope.Cluster); + + ConfigKey KvmCpuDynamicScalingCapacity = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, + Integer.class, "kvm.cpu.dynamic.scaling.capacity", "0", + "Defines the maximum vCPU capacity for which VMs can be dynamically scaled to with KVM. " + + "The 'kvm.cpu.dynamic.scaling.capacity' setting's value will be used to define the value of the " + + "'' element of domain XMLs. If it is set to a value less than or equal to '0', then the host's CPU cores capacity will be considered.", + true, ConfigKey.Scope.Cluster); + public boolean releaseVmCapacity(VirtualMachine vm, boolean moveFromReserved, boolean moveToReservered, Long hostId); void allocateVmCapacity(VirtualMachine vm, boolean fromLastHost); diff --git a/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java b/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java index eebf764289db..5909d098db8b 100644 --- a/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java +++ b/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java @@ -56,10 +56,10 @@ */ public interface ConfigurationManager { - public static final String MESSAGE_CREATE_POD_IP_RANGE_EVENT = "Message.CreatePodIpRange.Event"; - public static final String MESSAGE_DELETE_POD_IP_RANGE_EVENT = "Message.DeletePodIpRange.Event"; - public static final String MESSAGE_CREATE_VLAN_IP_RANGE_EVENT = "Message.CreateVlanIpRange.Event"; - public static final String MESSAGE_DELETE_VLAN_IP_RANGE_EVENT = "Message.DeleteVlanIpRange.Event"; + String MESSAGE_CREATE_POD_IP_RANGE_EVENT = "Message.CreatePodIpRange.Event"; + String MESSAGE_DELETE_POD_IP_RANGE_EVENT = "Message.DeletePodIpRange.Event"; + String MESSAGE_CREATE_VLAN_IP_RANGE_EVENT = "Message.CreateVlanIpRange.Event"; + String MESSAGE_DELETE_VLAN_IP_RANGE_EVENT = "Message.DeleteVlanIpRange.Event"; public static final ConfigKey AllowNonRFC1918CompliantIPs = new ConfigKey<>(Boolean.class, "allow.non.rfc1918.compliant.ips", "Advanced", "false", @@ -72,8 +72,9 @@ public interface ConfigurationManager { true, ConfigKey.Scope.Global); /** - * @param offering - * @return + * Is this for a VPC + * @param offering the offering to check + * @return true or false */ boolean isOfferingForVpc(NetworkOffering offering); @@ -83,80 +84,12 @@ public interface ConfigurationManager { /** * Updates a configuration entry with a new value - * @param userId - * @param name - * @param category - * @param value - * @param scope - * @param id */ String updateConfiguration(long userId, String name, String category, String value, ConfigKey.Scope scope, Long id); -// /** -// * Creates a new service offering -// * -// * @param name -// * @param cpu -// * @param ramSize -// * @param speed -// * @param displayText -// * @param localStorageRequired -// * @param offerHA -// * @param domainId -// * @param volatileVm -// * @param hostTag -// * @param networkRate -// * @param id -// * @param useVirtualNetwork -// * @param deploymentPlanner -// * @param details -// * @param bytesReadRate -// * @param bytesWriteRate -// * @param iopsReadRate -// * @param iopsWriteRate -// * @return ID -// */ -// ServiceOfferingVO createServiceOffering(long userId, boolean isSystem, VirtualMachine.Type vm_typeType, String name, int cpu, int ramSize, int speed, String displayText, boolean localStorageRequired, -// boolean offerHA, boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag, Integer networkRate, String deploymentPlanner, Map details, -// Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate); - -// /** -// * Creates a new disk offering -// * -// * @param domainId -// * @param name -// * @param description -// * @param numGibibytes -// * @param tags -// * @param isCustomized -// * @param localStorageRequired -// * @param isDisplayOfferingEnabled -// * @param isCustomizedIops (is admin allowing users to set custom iops?) -// * @param minIops -// * @param maxIops -// * @param bytesReadRate -// * @param bytesWriteRate -// * @param iopsReadRate -// * @param iopsWriteRate -// * @return newly created disk offering -// */ -// DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized, -// boolean localStorageRequired, boolean isDisplayOfferingEnabled, Boolean isCustomizedIops, Long minIops, Long maxIops, -// Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate); - /** * Creates a new pod - * - * @param userId - * @param podName - * @param zone - * @param gateway - * @param cidr - * @param startIp - * @param endIp - * @param allocationState * @param skipGatewayOverlapCheck (true if it is ok to not validate that gateway IP address overlap with Start/End IP of the POD) - * @param storageAccessGroups * @return Pod */ HostPodVO createPod(long userId, String podName, DataCenter zone, String gateway, String cidr, String startIp, String endIp, String allocationState, @@ -164,7 +97,6 @@ HostPodVO createPod(long userId, String podName, DataCenter zone, String gateway /** * Creates a new zone - * * @param userId * @param zoneName * @param dns1 @@ -172,16 +104,18 @@ HostPodVO createPod(long userId, String podName, DataCenter zone, String gateway * @param internalDns1 * @param internalDns2 * @param guestCidr + * @param domain + * @param domainId * @param zoneType * @param allocationState * @param networkDomain * @param isSecurityGroupEnabled + * @param isLocalStorageEnabled * @param ip6Dns1 * @param ip6Dns2 + * @param isEdge * @param storageAccessGroups * @return - * @throws - * @throws */ DataCenterVO createZone(long userId, String zoneName, String dns1, String dns2, String internalDns1, String internalDns2, String guestCidr, String domain, Long domainId, NetworkType zoneType, String allocationState, String networkDomain, boolean isSecurityGroupEnabled, boolean isLocalStorageEnabled, String ip6Dns1, @@ -191,9 +125,7 @@ DataCenterVO createZone(long userId, String zoneName, String dns1, String dns2, * Deletes a VLAN from the database, along with all of its IP addresses. Will not delete VLANs that have allocated * IP addresses. * - * @param userId - * @param vlanDbId - * @param caller + * @param caller TODO * @return success/failure */ VlanVO deleteVlanAndPublicIpRange(long userId, long vlanDbId, Account caller); @@ -204,31 +136,39 @@ DataCenterVO createZone(long userId, String zoneName, String dns1, String dns2, /** * Creates a new network offering - * * @param name * @param displayText * @param trafficType * @param tags * @param specifyVlan + * @param availability * @param networkRate * @param serviceProviderMap * @param isDefault * @param type * @param systemOnly * @param serviceOfferingId - * @param conserveMode ; + * @param conserveMode + * @param serviceCapabilityMap * @param specifyIpRanges - * @param isPersistent ; + * @param isPersistent * @param details + * @param egressDefaultPolicy + * @param maxconn + * @param enableKeepAlive * @param forVpc * @param forTungsten * @param forNsx * @param forNetris + * @param networkMode * @param domainIds * @param zoneIds - * @return network offering object + * @param enableOffering + * @param internetProtocol + * @param routingMode + * @param specifyAsNumber + * @return the network offering */ - NetworkOfferingVO createNetworkOffering(String name, String displayText, TrafficType trafficType, String tags, boolean specifyVlan, Availability availability, Integer networkRate, Map> serviceProviderMap, boolean isDefault, Network.GuestType type, boolean systemOnly, Long serviceOfferingId, boolean conserveMode, Map> serviceCapabilityMap, boolean specifyIpRanges, boolean isPersistent, @@ -245,7 +185,6 @@ Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetwor /** * Release dedicated virtual ip ranges of a domain. * - * @param domainId * @return success/failure */ boolean releaseDomainSpecificVirtualRanges(Domain domain); @@ -253,7 +192,6 @@ Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetwor /** * Release dedicated virtual ip ranges of an account. * - * @param accountId * @return success/failure */ boolean releaseAccountSpecificVirtualRanges(Account account); @@ -261,16 +199,7 @@ Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetwor /** * Edits a pod in the database. Will not allow you to edit pods that are being used anywhere in the system. * - * @param id - * @param name - * @param startIp - * @param endIp - * @param gateway - * @param netmask - * @param allocationState * @return Pod - * @throws - * @throws */ Pod editPod(long id, String name, String startIp, String endIp, String gateway, String netmask, String allocationState); diff --git a/engine/components-api/src/main/java/com/cloud/event/UsageEventUtils.java b/engine/components-api/src/main/java/com/cloud/event/UsageEventUtils.java index 94fbb7a80af8..1c88c7df124c 100644 --- a/engine/components-api/src/main/java/com/cloud/event/UsageEventUtils.java +++ b/engine/components-api/src/main/java/com/cloud/event/UsageEventUtils.java @@ -94,6 +94,14 @@ public static void publishUsageEvent(String usageType, long accountId, long zone } + public static void publishUsageEvent(String usageType, long accountId, long zoneId, long resourceId, String resourceName, Long offeringId, Long templateId, + Long size, String entityType, String entityUUID, Long vmId, boolean displayResource) { + if (displayResource) { + saveUsageEvent(usageType, accountId, zoneId, resourceId, offeringId, templateId, size, vmId, resourceName); + } + publishUsageEvent(usageType, accountId, zoneId, entityType, entityUUID); + } + public static void publishUsageEvent(String usageType, long accountId, long zoneId, long resourceId, String resourceName, Long offeringId, Long templateId, Long size, Long virtualSize, String entityType, String entityUUID, Map details) { saveUsageEvent(usageType, accountId, zoneId, resourceId, resourceName, offeringId, templateId, size, virtualSize, details); @@ -202,6 +210,10 @@ public static void saveUsageEvent(String usageType, long accountId, long zoneId, s_usageEventDao.persist(new UsageEventVO(usageType, accountId, zoneId, vmId, securityGroupId)); } + public static void saveUsageEvent(String usageType, long accountId, long zoneId, long resourceId, Long offeringId, Long templateId, Long size, Long vmId, String resourceName) { + s_usageEventDao.persist(new UsageEventVO(usageType, accountId, zoneId, resourceId, offeringId, templateId, size, vmId, resourceName)); + } + private static void publishUsageEvent(String usageEventType, Long accountId, Long zoneId, String resourceType, String resourceUUID) { String configKey = "publish.usage.events"; String value = s_configDao.getValue(configKey); diff --git a/engine/components-api/src/main/java/com/cloud/ha/HighAvailabilityManager.java b/engine/components-api/src/main/java/com/cloud/ha/HighAvailabilityManager.java index ddc8153d7398..53bfcce27038 100644 --- a/engine/components-api/src/main/java/com/cloud/ha/HighAvailabilityManager.java +++ b/engine/components-api/src/main/java/com/cloud/ha/HighAvailabilityManager.java @@ -21,6 +21,7 @@ import com.cloud.deploy.DeploymentPlanner; import com.cloud.host.HostVO; import com.cloud.host.Status; +import com.cloud.storage.Storage.StoragePoolType; import com.cloud.utils.component.Manager; import com.cloud.vm.VMInstanceVO; import org.apache.cloudstack.framework.config.ConfigKey; @@ -32,6 +33,8 @@ */ public interface HighAvailabilityManager extends Manager { + List LIBVIRT_STORAGE_POOL_TYPES_WITH_HA_SUPPORT = List.of(StoragePoolType.NetworkFilesystem, StoragePoolType.SharedMountPoint); + ConfigKey ForceHA = new ConfigKey<>("Advanced", Boolean.class, "force.ha", "false", "Force High-Availability to happen even if the VM says no.", true, Cluster); @@ -72,10 +75,10 @@ public interface HighAvailabilityManager extends Manager { + " which are registered for the HA event that were successful and are now ready to be purged.", true, Cluster); - public static final ConfigKey KvmHAFenceHostIfHeartbeatFailsOnStorage = new ConfigKey<>("Advanced", Boolean.class, "kvm.ha.fence.on.storage.heartbeat.failure", "false", + ConfigKey KvmHAFenceHostIfHeartbeatFailsOnStorage = new ConfigKey<>("Advanced", Boolean.class, "kvm.ha.fence.on.storage.heartbeat.failure", "false", "Proceed fencing the host even the heartbeat failed for only one storage pool", false, ConfigKey.Scope.Zone); - public enum WorkType { + enum WorkType { Migration, // Migrating VMs off of a host. Stop, // Stops a VM for storage pool migration purposes. This should be obsolete now. CheckStop, // Checks if a VM has been stopped. diff --git a/engine/components-api/src/main/java/com/cloud/hypervisor/ExternalProvisioner.java b/engine/components-api/src/main/java/com/cloud/hypervisor/ExternalProvisioner.java index a22ea4211132..c574a8be0175 100644 --- a/engine/components-api/src/main/java/com/cloud/hypervisor/ExternalProvisioner.java +++ b/engine/components-api/src/main/java/com/cloud/hypervisor/ExternalProvisioner.java @@ -18,6 +18,8 @@ import java.util.Map; +import com.cloud.agent.api.GetExternalConsoleAnswer; +import com.cloud.agent.api.GetExternalConsoleCommand; import com.cloud.agent.api.HostVmStateReportEntry; import com.cloud.agent.api.PrepareExternalProvisioningAnswer; import com.cloud.agent.api.PrepareExternalProvisioningCommand; @@ -57,5 +59,7 @@ public interface ExternalProvisioner extends Manager { Map getHostVmStateReport(long hostId, String extensionName, String extensionRelativePath); + GetExternalConsoleAnswer getInstanceConsole(String hostGuid, String extensionName, String extensionRelativePath, GetExternalConsoleCommand cmd); + RunCustomActionAnswer runCustomAction(String hostGuid, String extensionName, String extensionRelativePath, RunCustomActionCommand cmd); } diff --git a/engine/components-api/src/main/java/com/cloud/network/IpAddressManager.java b/engine/components-api/src/main/java/com/cloud/network/IpAddressManager.java index b1cad20b19ec..454cb10a2f2b 100644 --- a/engine/components-api/src/main/java/com/cloud/network/IpAddressManager.java +++ b/engine/components-api/src/main/java/com/cloud/network/IpAddressManager.java @@ -288,4 +288,6 @@ List listAvailablePublicIps(final long dcId, PublicIpQuarantine updatePublicIpAddressInQuarantine(Long quarantineProcessId, Date endDate); void updateSourceNatIpAddress(IPAddressVO requestedIp, List userIps) throws Exception; + + Long getPreferredNetworkIdForPublicIpRuleAssignment(IpAddress ip, Long networkId); } diff --git a/engine/components-api/src/main/java/com/cloud/network/addr/PublicIp.java b/engine/components-api/src/main/java/com/cloud/network/addr/PublicIp.java index 7132094ce117..54a99fdf3677 100644 --- a/engine/components-api/src/main/java/com/cloud/network/addr/PublicIp.java +++ b/engine/components-api/src/main/java/com/cloud/network/addr/PublicIp.java @@ -40,7 +40,11 @@ public PublicIp(IPAddressVO addr, VlanVO vlan, long macAddress) { } public static PublicIp createFromAddrAndVlan(IPAddressVO addr, VlanVO vlan) { - return new PublicIp(addr, vlan, NetUtils.createSequenceBasedMacAddress(addr.getMacAddress(), NetworkModel.MACIdentifier.value())); + long macIdentifier = NetworkModel.MACIdentifier.valueIn(addr.getDataCenterId()); + if (macIdentifier == 0) { + macIdentifier = addr.getDataCenterId(); + } + return new PublicIp(addr, vlan, NetUtils.createSequenceBasedMacAddress(addr.getMacAddress(), macIdentifier)); } @Override @@ -279,5 +283,4 @@ public boolean isForSystemVms() { public boolean isForRouter() { return _addr.isForRouter(); } - } diff --git a/engine/components-api/src/main/java/com/cloud/network/lb/LoadBalancingRulesManager.java b/engine/components-api/src/main/java/com/cloud/network/lb/LoadBalancingRulesManager.java index 669456cbdcc2..d8011e9ade12 100644 --- a/engine/components-api/src/main/java/com/cloud/network/lb/LoadBalancingRulesManager.java +++ b/engine/components-api/src/main/java/com/cloud/network/lb/LoadBalancingRulesManager.java @@ -39,7 +39,7 @@ public interface LoadBalancingRulesManager { LoadBalancer createPublicLoadBalancer(String xId, String name, String description, int srcPort, int destPort, long sourceIpId, String protocol, String algorithm, - boolean openFirewall, CallContext caller, String lbProtocol, Boolean forDisplay, String cidrList) throws NetworkRuleConflictException; + boolean openFirewall, CallContext caller, String lbProtocol, Boolean forDisplay, String cidrList, Long networkId) throws NetworkRuleConflictException; boolean removeAllLoadBalanacersForIp(long ipId, Account caller, long callerUserId); diff --git a/engine/components-api/src/main/java/com/cloud/network/rules/StaticNatRuleImpl.java b/engine/components-api/src/main/java/com/cloud/network/rules/StaticNatRuleImpl.java index 4d8270ca078d..980606176024 100644 --- a/engine/components-api/src/main/java/com/cloud/network/rules/StaticNatRuleImpl.java +++ b/engine/components-api/src/main/java/com/cloud/network/rules/StaticNatRuleImpl.java @@ -80,10 +80,15 @@ public long getDomainId() { } @Override - public long getNetworkId() { + public Long getNetworkId() { return networkId; } + @Override + public Long getVpcId() { + return null; + } + @Override public long getId() { return id; diff --git a/engine/components-api/src/main/java/com/cloud/network/vpc/VpcManager.java b/engine/components-api/src/main/java/com/cloud/network/vpc/VpcManager.java index e7f41d079a74..792a3a6b397f 100644 --- a/engine/components-api/src/main/java/com/cloud/network/vpc/VpcManager.java +++ b/engine/components-api/src/main/java/com/cloud/network/vpc/VpcManager.java @@ -211,4 +211,9 @@ public interface VpcManager { void reconfigStaticNatForVpcVr(Long vpcId); boolean applyStaticRouteForVpcVpnIfNeeded(Long vpcId, boolean updateAllVpn) throws ResourceUnavailableException; + + /** + * Returns true if the network is part of a VPC, and the VPC is created from conserve mode enabled VPC offering + */ + boolean isNetworkOnVpcEnabledConserveMode(Network network); } diff --git a/engine/components-api/src/main/java/com/cloud/resource/ResourceManager.java b/engine/components-api/src/main/java/com/cloud/resource/ResourceManager.java index 8550dfdd9067..4767e86e8ab8 100755 --- a/engine/components-api/src/main/java/com/cloud/resource/ResourceManager.java +++ b/engine/components-api/src/main/java/com/cloud/resource/ResourceManager.java @@ -57,8 +57,8 @@ public interface ResourceManager extends ResourceService, Configurable { ConfigKey KvmSshToAgentEnabled = new ConfigKey<>("Advanced", Boolean.class, "kvm.ssh.to.agent","true", - "Number of retries when preparing a host into Maintenance Mode is faulty before failing", - false); + "True if the management server will restart the agent service via SSH into the KVM hosts after or during maintenance operations", + true); ConfigKey HOST_MAINTENANCE_LOCAL_STRATEGY = new ConfigKey<>(String.class, "host.maintenance.local.storage.strategy", "Advanced","Error", @@ -79,6 +79,11 @@ public interface ResourceManager extends ResourceService, Configurable { ConfigKey.Kind.Select, "," + CPU.CPUArch.getTypesAsCSV()); + ConfigKey SystemVMDefaultHypervisor = new ConfigKey(String.class, + "system.vm.default.hypervisor", "Advanced", "Any", "Hypervisor type used to create System VMs. Valid values are: XenServer, KVM, VMware, Hyperv, VirtualBox, " + + "Parralels, BareMetal, Ovm, LXC, Any", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.Select, "XenServer, KVM, VMware, Hyperv, " + + "VirtualBox, Parralels, BareMetal, Ovm, LXC, Any"); + /** * Register a listener for different types of resource life cycle events. * There can only be one type of listener per type of host. @@ -117,6 +122,8 @@ public interface ResourceManager extends ResourceService, Configurable { public boolean executeUserRequest(long hostId, ResourceState.Event event) throws AgentUnavailableException; + boolean executeUserRequest(long hostId, ResourceState.Event event, boolean isForced, boolean isForceDeleteStorage) throws AgentUnavailableException; + boolean resourceStateTransitTo(Host host, Event event, long msId) throws NoTransitionException; boolean umanageHost(long hostId); @@ -162,6 +169,8 @@ public interface ResourceManager extends ResourceService, Configurable { public HostVO findHostByGuid(String guid); + HostVO findHostByGuidPrefix(String guid); + public HostVO findHostByName(String name); HostStats getHostStatistics(Host host); diff --git a/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java b/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java index d3a6d4525a5e..032fcbe76dce 100644 --- a/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java +++ b/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java @@ -161,28 +161,28 @@ public interface StorageManager extends StorageService { ConfigKey.Scope.StoragePool, null); - ConfigKey PRIMARY_STORAGE_DOWNLOAD_WAIT = new ConfigKey("Storage", Integer.class, "primary.storage.download.wait", "10800", + ConfigKey PRIMARY_STORAGE_DOWNLOAD_WAIT = new ConfigKey<>("Storage", Integer.class, "primary.storage.download.wait", "10800", "In second, timeout for download template to primary storage", false); - ConfigKey SecStorageMaxMigrateSessions = new ConfigKey("Advanced", Integer.class, "secstorage.max.migrate.sessions", "2", + ConfigKey SecStorageMaxMigrateSessions = new ConfigKey<>("Advanced", Integer.class, "secstorage.max.migrate.sessions", "2", "The max number of concurrent copy command execution sessions that an SSVM can handle", false, ConfigKey.Scope.Global); - ConfigKey SecStorageVMAutoScaleDown = new ConfigKey("Advanced", Boolean.class, "secstorage.vm.auto.scale.down", "false", + ConfigKey SecStorageVMAutoScaleDown = new ConfigKey<>("Advanced", Boolean.class, "secstorage.vm.auto.scale.down", "false", "Setting this to 'true' will auto scale down SSVMs", true, ConfigKey.Scope.Global); - ConfigKey MaxDataMigrationWaitTime = new ConfigKey("Advanced", Integer.class, "max.data.migration.wait.time", "15", + ConfigKey MaxDataMigrationWaitTime = new ConfigKey<>("Advanced", Integer.class, "max.data.migration.wait.time", "15", "Maximum wait time (in minutes) for a data migration task before spawning a new SSVM", false, ConfigKey.Scope.Global); - ConfigKey DiskProvisioningStrictness = new ConfigKey("Storage", Boolean.class, "disk.provisioning.type.strictness", "false", + ConfigKey DiskProvisioningStrictness = new ConfigKey<>("Storage", Boolean.class, "disk.provisioning.type.strictness", "false", "If set to true, the disk is created only when there is a suitable storage pool that supports the disk provisioning type specified by the service/disk offering. " + "If set to false, the disk is created with a disk provisioning type supported by the pool. Default value is false, and this is currently supported for VMware only.", true, ConfigKey.Scope.Zone); - ConfigKey PreferredStoragePool = new ConfigKey(String.class, "preferred.storage.pool", "Advanced", "", + ConfigKey PreferredStoragePool = new ConfigKey<>(String.class, "preferred.storage.pool", "Advanced", "", "The UUID of preferred storage pool for allocation.", true, ConfigKey.Scope.Account, null); ConfigKey MountDisabledStoragePool = new ConfigKey<>(Boolean.class, "mount.disabled.storage.pool", "Storage", - "false", + Boolean.TRUE.toString(), "Mount all zone-wide or cluster-wide disabled storage pools after node reboot", true, ConfigKey.Scope.Cluster, @@ -195,6 +195,14 @@ public interface StorageManager extends StorageService { true, ConfigKey.Scope.StoragePool, null); + ConfigKey XenserverCreateCloneFull = new ConfigKey<>(Boolean.class, + "xenserver.create.full.clone", + "Storage", + "false", + "If set to true, creates VMs as full clones on XenServer hypervisor (uses VDI.copy instead of VDI.clone, removing the linked-clone parent relationship).", + true, + ConfigKey.Scope.StoragePool, + null); ConfigKey VmwareAllowParallelExecution = new ConfigKey<>(Boolean.class, "vmware.allow.parallel.command.execution", "Advanced", @@ -203,7 +211,7 @@ public interface StorageManager extends StorageService { true, ConfigKey.Scope.Global, null); - static final ConfigKey DataStoreDownloadFollowRedirects = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, + ConfigKey DataStoreDownloadFollowRedirects = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Boolean.class, "store.download.follow.redirects", "false", "Whether HTTP redirect is followed during store downloads for objects such as template, volume etc.", true, ConfigKey.Scope.Global); @@ -211,7 +219,7 @@ public interface StorageManager extends StorageService { ConfigKey HEURISTICS_SCRIPT_TIMEOUT = new ConfigKey<>("Advanced", Long.class, "heuristics.script.timeout", "3000", "The maximum runtime, in milliseconds, to execute the heuristic rule; if it is reached, a timeout will happen.", true); - ConfigKey AllowVolumeReSizeBeyondAllocation = new ConfigKey("Advanced", Boolean.class, "volume.resize.allowed.beyond.allocation", "false", + ConfigKey AllowVolumeReSizeBeyondAllocation = new ConfigKey<>("Advanced", Boolean.class, "volume.resize.allowed.beyond.allocation", "false", "Determines whether volume size can exceed the pool capacity allocation disable threshold (pool.storage.allocated.capacity.disablethreshold) " + "when resize a volume upto resize capacity disable threshold (pool.storage.allocated.resize.capacity.disablethreshold)", true, List.of(ConfigKey.Scope.StoragePool, ConfigKey.Scope.Zone)); @@ -228,9 +236,17 @@ public interface StorageManager extends StorageService { ConfigKey.Scope.Global, null); + ConfigKey COPY_TEMPLATES_FROM_OTHER_SECONDARY_STORAGES = new ConfigKey<>(Boolean.class, "copy.templates.from.other.secondary.storages", + "Storage", "true", "When enabled, this feature allows templates to be copied from existing Secondary Storage servers (within the same zone or across zones) " + + "while adding a new Secondary Storage. If the copy operation fails, the system falls back to downloading the template from the source URL.", + true, ConfigKey.Scope.Zone, null); + + ConfigKey AgentMaxDataMigrationWaitTime = new ConfigKey<>("Advanced", Integer.class, "agent.max.data.migration.wait.time", "3600", + "The maximum time (in seconds) that the secondary storage data migration command sent to the KVM Agent will be executed before a timeout occurs.", true, ConfigKey.Scope.Cluster); + /** * should we execute in sequence not involving any storages? - * @return tru if commands should execute in sequence + * @return true if commands should execute in sequence */ static boolean shouldExecuteInSequenceOnVmware() { return shouldExecuteInSequenceOnVmware(null, null); @@ -252,14 +268,14 @@ static Boolean getFullCloneConfiguration(Long storeId) { /** * Returns a comma separated list of tags for the specified storage pool - * @param poolId + * @param poolId the id of the pool to get tags for * @return comma separated list of tags */ String getStoragePoolTags(long poolId); /** * Returns a list of Strings with tags for the specified storage pool - * @param poolId + * @param poolId the id of the pool to get tags for * @return comma separated list of tags */ List getStoragePoolTagList(long poolId); @@ -276,7 +292,7 @@ static Boolean getFullCloneConfiguration(Long storeId) { Pair sendToPool(StoragePool pool, long[] hostIdsToTryFirst, List hostIdsToAvoid, Command cmd) throws StorageUnavailableException; - public Answer getVolumeStats(StoragePool pool, Command cmd); + Answer getVolumeStats(StoragePool pool, Command cmd); boolean canPoolProvideStorageStats(StoragePool pool); @@ -302,6 +318,8 @@ static Boolean getFullCloneConfiguration(Long storeId) { Answer sendToPool(StoragePool pool, long[] hostIdsToTryFirst, Command cmd) throws StorageUnavailableException; + void updateStoragePoolHostVOAndBytes(StoragePool pool, long hostId, ModifyStoragePoolAnswer mspAnswer); + CapacityVO getSecondaryStorageUsedStats(Long hostId, Long zoneId); CapacityVO getStoragePoolUsedStats(Long poolId, Long clusterId, Long podId, Long zoneId); diff --git a/engine/components-api/src/main/java/com/cloud/template/TemplateManager.java b/engine/components-api/src/main/java/com/cloud/template/TemplateManager.java index 28f41c677cb5..8c11fe6c93ad 100644 --- a/engine/components-api/src/main/java/com/cloud/template/TemplateManager.java +++ b/engine/components-api/src/main/java/com/cloud/template/TemplateManager.java @@ -45,9 +45,11 @@ public interface TemplateManager { static final String AllowPublicUserTemplatesCK = "allow.public.user.templates"; static final String TemplatePreloaderPoolSizeCK = "template.preloader.pool.size"; + static final String PublicTemplateSecStorageCopyCK = "secstorage.public.template.copy.max"; + static final String PrivateTemplateSecStorageCopyCK = "secstorage.private.template.copy.max"; static final ConfigKey AllowPublicUserTemplates = new ConfigKey("Advanced", Boolean.class, AllowPublicUserTemplatesCK, "true", - "If false, users will not be able to create public templates.", true, ConfigKey.Scope.Account); + "If false, users will not be able to create public Templates.", true, ConfigKey.Scope.Account); static final ConfigKey TemplatePreloaderPoolSize = new ConfigKey("Advanced", Integer.class, TemplatePreloaderPoolSizeCK, "8", "Size of the TemplateManager threadpool", false, ConfigKey.Scope.Global); @@ -57,6 +59,40 @@ public interface TemplateManager { + "will validate if the provided URL is resolvable during the register of templates/ISOs before persisting them in the database.", true); + ConfigKey TemplateDeleteFromPrimaryStorage = new ConfigKey("Advanced", + Boolean.class, + "template.delete.from.primary.storage", "true", + "Template when deleted will be instantly deleted from the Primary Storage", + true, + ConfigKey.Scope.Global); + + ConfigKey PublicTemplateSecStorageCopy = new ConfigKey("Advanced", Integer.class, + PublicTemplateSecStorageCopyCK, "0", + "Maximum number of secondary storage pools to which a public template is copied. " + + "0 means copy to all secondary storage pools (default behavior).", + true, ConfigKey.Scope.Zone); + + ConfigKey PrivateTemplateSecStorageCopy = new ConfigKey("Advanced", Integer.class, + PrivateTemplateSecStorageCopyCK, "1", + "Maximum number of secondary storage pools to which a private template is copied. " + + "Default is 1 to preserve existing behavior.", + true, ConfigKey.Scope.Zone); + + ConfigKey VmIsoMaxCount = new ConfigKey("Advanced", + Integer.class, + "vm.iso.max.count", "1", + "Maximum number of ISOs that may be attached to a VM.", + true, + ConfigKey.Scope.Cluster); + + // KVM/libvirt maps deviceSeq=3 to hdc (hda/hdb are taken by the root volume on i440fx/IDE). + // user_vm.iso_id has always pointed at this slot; additional cdroms live in vm_iso_map. + int CDROM_PRIMARY_DEVICE_SEQ = 3; + + // Fallback per-VM cdrom cap when the placement host hasn't advertised host.cdrom.max.count + // (older agent, never-deployed VM, etc.). + int DEFAULT_CDROM_MAX_PER_VM = 1; + static final String VMWARE_TOOLS_ISO = "vmware-tools.iso"; static final String XS_TOOLS_ISO = "xs-tools.iso"; @@ -104,6 +140,8 @@ public interface TemplateManager { */ List getUnusedTemplatesInPool(StoragePoolVO pool); + void evictTemplateFromStoragePoolsForZones(Long templateId, List zoneId); + /** * Deletes a template in the specified storage pool. * @@ -129,6 +167,12 @@ public interface TemplateManager { List getImageStoreByTemplate(long templateId, Long zoneId); + /** + * Max number of secondary storage copies for the template in this zone; {@code 0} means no limit. + * SYSTEM/ROUTING/BUILTIN templates are always exempt (returns {@code 0}). + */ + int getSecStorageCopyLimit(VMTemplateVO template, long zoneId); + TemplateInfo prepareIso(long isoId, long dcId, Long hostId, Long poolId); @@ -144,6 +188,8 @@ public interface TemplateManager { TemplateType validateTemplateType(BaseCmd cmd, boolean isAdmin, boolean isCrossZones, Hypervisor.HypervisorType hypervisorType); + DataStore verifyHeuristicRulesForZone(VMTemplateVO template, Long zoneId); + List getTemplateDisksOnImageStore(VirtualMachineTemplate template, DataStoreRole role, String configurationId); static Boolean getValidateUrlIsResolvableBeforeRegisteringTemplateValue() { diff --git a/engine/components-api/src/main/java/com/cloud/vm/VmWorkDeleteBackup.java b/engine/components-api/src/main/java/com/cloud/vm/VmWorkDeleteBackup.java new file mode 100644 index 000000000000..b9d2907ef780 --- /dev/null +++ b/engine/components-api/src/main/java/com/cloud/vm/VmWorkDeleteBackup.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.vm; + +public class VmWorkDeleteBackup extends VmWork { + + private long backupId; + + private boolean forced; + + public VmWorkDeleteBackup(long userId, long accountId, long vmId, String handlerName, long backupId, boolean forced) { + super(userId, accountId, vmId, handlerName); + this.backupId = backupId; + this.forced = forced; + } + + public long getBackupId() { + return backupId; + } + + public boolean isForced() { + return forced; + } +} diff --git a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ResourceTest.java b/engine/components-api/src/main/java/com/cloud/vm/VmWorkRestoreBackup.java similarity index 55% rename from framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ResourceTest.java rename to engine/components-api/src/main/java/com/cloud/vm/VmWorkRestoreBackup.java index cdcfc87cd4e4..421430cfbe9d 100644 --- a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ResourceTest.java +++ b/engine/components-api/src/main/java/com/cloud/vm/VmWorkRestoreBackup.java @@ -14,27 +14,32 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. +package com.cloud.vm; -package org.apache.cloudstack.quota.activationrule.presetvariables; +public class VmWorkRestoreBackup extends VmWork { -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; + private long backupId; -@RunWith(MockitoJUnitRunner.class) -public class ResourceTest { + private boolean quickRestore; - @Test - public void toStringTestReturnAJson() { - Resource variable = new Resource(); + private Long hostId; - String expected = ToStringBuilder.reflectionToString(variable, ToStringStyle.JSON_STYLE); - String result = variable.toString(); + public VmWorkRestoreBackup(long userId, long accountId, long vmId, String handlerName, long backupId, boolean quickRestore, Long hostId) { + super(userId, accountId, vmId, handlerName); + this.backupId = backupId; + this.quickRestore = quickRestore; + this.hostId = hostId; + } + + public long getBackupId() { + return backupId; + } - Assert.assertEquals(expected, result); + public boolean isQuickRestore() { + return quickRestore; } + public Long getHostId() { + return hostId; + } } diff --git a/engine/components-api/src/main/java/com/cloud/vm/VmWorkRestoreVolumeBackupAndAttach.java b/engine/components-api/src/main/java/com/cloud/vm/VmWorkRestoreVolumeBackupAndAttach.java new file mode 100644 index 000000000000..a34b11abbdb4 --- /dev/null +++ b/engine/components-api/src/main/java/com/cloud/vm/VmWorkRestoreVolumeBackupAndAttach.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.vm; + +import org.apache.cloudstack.backup.Backup; + +public class VmWorkRestoreVolumeBackupAndAttach extends VmWork { + + private long backupId; + + private Backup.VolumeInfo backupVolumeInfo; + + private String hostIp; + + private boolean quickRestore; + + public VmWorkRestoreVolumeBackupAndAttach(long userId, long accountId, long vmId, String handlerName, long backupId, Backup.VolumeInfo backupVolumeInfo, + String hostIp, boolean quickRestore) { + super(userId, accountId, vmId, handlerName); + this.backupId = backupId; + this.backupVolumeInfo = backupVolumeInfo; + this.hostIp = hostIp; + this.quickRestore = quickRestore; + } + + public long getBackupId() { + return backupId; + } + + public Backup.VolumeInfo getBackupVolumeInfo() { + return backupVolumeInfo; + } + + public String getHostIp() { + return hostIp; + } + + public boolean isQuickRestore() { + return quickRestore; + } +} diff --git a/engine/components-api/src/main/java/com/cloud/vm/VmWorkSerializer.java b/engine/components-api/src/main/java/com/cloud/vm/VmWorkSerializer.java index bd6b52d261fa..e4fdc0c4f375 100644 --- a/engine/components-api/src/main/java/com/cloud/vm/VmWorkSerializer.java +++ b/engine/components-api/src/main/java/com/cloud/vm/VmWorkSerializer.java @@ -61,7 +61,6 @@ public static String serialize(VmWork work) { // use java binary serialization instead // return JobSerializerHelper.toObjectSerializedString(work); - // return s_gson.toJson(work); } public static T deserialize(Class clazz, String workInJsonText) { @@ -69,6 +68,5 @@ public static T deserialize(Class clazz, String workInJson // use java binary serialization instead // return (T)JobSerializerHelper.fromObjectSerializedString(workInJsonText); - // return (T)s_gson.fromJson(workInJsonText, clazz); } } diff --git a/engine/components-api/src/main/java/com/cloud/vm/VmWorkTakeBackup.java b/engine/components-api/src/main/java/com/cloud/vm/VmWorkTakeBackup.java new file mode 100644 index 000000000000..57367d368b86 --- /dev/null +++ b/engine/components-api/src/main/java/com/cloud/vm/VmWorkTakeBackup.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.vm; + +public class VmWorkTakeBackup extends VmWork { + + private long backupId; + + private boolean quiesceVm; + + private boolean isolated; + + public VmWorkTakeBackup(long userId, long accountId, long vmId, long backupId, String handlerName, boolean quiesceVm, boolean isolated) { + super(userId, accountId, vmId, handlerName); + this.quiesceVm = quiesceVm; + this.backupId = backupId; + this.isolated = isolated; + } + + public boolean isQuiesceVm() { + return quiesceVm; + } + + public long getBackupId() { + return backupId; + } + + public boolean isIsolated() { + return isolated; + } + + @Override + public String toString() { + return super.toStringAfterRemoveParams(null, null); + } +} diff --git a/engine/components-api/src/main/java/com/cloud/vm/snapshot/VMSnapshotManager.java b/engine/components-api/src/main/java/com/cloud/vm/snapshot/VMSnapshotManager.java index a01d4ee5cae7..6831552b83db 100644 --- a/engine/components-api/src/main/java/com/cloud/vm/snapshot/VMSnapshotManager.java +++ b/engine/components-api/src/main/java/com/cloud/vm/snapshot/VMSnapshotManager.java @@ -31,7 +31,7 @@ public interface VMSnapshotManager extends VMSnapshotService, Manager { static final ConfigKey VMSnapshotExpireInterval = new ConfigKey("Advanced", Integer.class, "vmsnapshot.expire.interval", "-1", "VM Snapshot expire interval in hours", true, ConfigKey.Scope.Account); - ConfigKey VMSnapshotMax = new ConfigKey("Advanced", Integer.class, "vmsnapshot.max", "10", "Maximum vm snapshots for a single vm", true, ConfigKey.Scope.Global); + ConfigKey VMSnapshotMax = new ConfigKey("Advanced", Integer.class, "vmsnapshot.max", "10", "Maximum VM snapshots for a single VM", true, ConfigKey.Scope.Account); /** * Delete all VM snapshots belonging to one VM @@ -42,7 +42,7 @@ public interface VMSnapshotManager extends VMSnapshotService, Manager { boolean deleteAllVMSnapshots(long id, VMSnapshot.Type type); /** - * Sync VM snapshot state when VM snapshot in reverting or snapshoting or expunging state + * Sync VM snapshot state when VM snapshot in reverting or snapshotting or expunging state * Used for fullsync after agent connects * * @param vm, the VM in question @@ -54,5 +54,4 @@ public interface VMSnapshotManager extends VMSnapshotService, Manager { boolean hasActiveVMSnapshotTasks(Long vmId); RestoreVMSnapshotCommand createRestoreCommand(UserVmVO userVm, List vmSnapshotVOs); - } diff --git a/engine/orchestration/pom.xml b/engine/orchestration/pom.xml index c5040b6382b4..fda63d2558b0 100755 --- a/engine/orchestration/pom.xml +++ b/engine/orchestration/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.22.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT ../pom.xml 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 5483331cc337..1215829d92f8 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 @@ -42,6 +42,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.utils.StringUtils; import org.apache.cloudstack.agent.lb.IndirectAgentLB; import org.apache.cloudstack.ca.CAManager; import org.apache.cloudstack.command.ReconcileCommandService; @@ -64,7 +65,6 @@ 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; import com.cloud.agent.AgentManager; @@ -805,8 +805,11 @@ protected AgentAttache notifyMonitorsOfConnection(final AgentAttache attache, fi String uefiEnabled = detailsMap.get(Host.HOST_UEFI_ENABLE); String virtv2vVersion = detailsMap.get(Host.HOST_VIRTV2V_VERSION); String ovftoolVersion = detailsMap.get(Host.HOST_OVFTOOL_VERSION); + String vddkSupport = detailsMap.get(Host.HOST_VDDK_SUPPORT); + String vddkLibDir = detailsMap.get(Host.HOST_VDDK_LIB_DIR); + String vddkVersion = detailsMap.get(Host.HOST_VDDK_VERSION); logger.debug("Got HOST_UEFI_ENABLE [{}] for host [{}]:", uefiEnabled, host); - if (ObjectUtils.anyNotNull(uefiEnabled, virtv2vVersion, ovftoolVersion)) { + if (ObjectUtils.anyNotNull(uefiEnabled, virtv2vVersion, ovftoolVersion, vddkSupport, vddkLibDir, vddkVersion)) { _hostDao.loadDetails(host); boolean updateNeeded = false; if (StringUtils.isNotBlank(uefiEnabled) && !uefiEnabled.equals(host.getDetails().get(Host.HOST_UEFI_ENABLE))) { @@ -821,6 +824,26 @@ protected AgentAttache notifyMonitorsOfConnection(final AgentAttache attache, fi host.getDetails().put(Host.HOST_OVFTOOL_VERSION, ovftoolVersion); updateNeeded = true; } + if (StringUtils.isNotBlank(vddkSupport) && !vddkSupport.equals(host.getDetails().get(Host.HOST_VDDK_SUPPORT))) { + host.getDetails().put(Host.HOST_VDDK_SUPPORT, vddkSupport); + updateNeeded = true; + } + if (!StringUtils.defaultString(vddkLibDir).equals(StringUtils.defaultString(host.getDetails().get(Host.HOST_VDDK_LIB_DIR)))) { + if (StringUtils.isBlank(vddkLibDir)) { + host.getDetails().remove(Host.HOST_VDDK_LIB_DIR); + } else { + host.getDetails().put(Host.HOST_VDDK_LIB_DIR, vddkLibDir); + } + updateNeeded = true; + } + if (!StringUtils.defaultString(vddkVersion).equals(StringUtils.defaultString(host.getDetails().get(Host.HOST_VDDK_VERSION)))) { + if (StringUtils.isBlank(vddkVersion)) { + host.getDetails().remove(Host.HOST_VDDK_VERSION); + } else { + host.getDetails().put(Host.HOST_VDDK_VERSION, vddkVersion); + } + updateNeeded = true; + } if (updateNeeded) { _hostDao.saveDetails(host); } @@ -1652,7 +1675,6 @@ protected void processRequest(final Link link, final Request request) { final String reason = shutdown.getReason(); logger.info("Host {} has informed us that it is shutting down with reason {} and detail {}", attache, reason, shutdown.getDetail()); if (reason.equals(ShutdownCommand.Update)) { - // disconnectWithoutInvestigation(attache, Event.UpdateNeeded); throw new CloudRuntimeException("Agent update not implemented"); } else if (reason.equals(ShutdownCommand.Requested)) { disconnectWithoutInvestigation(attache, Event.ShutdownRequested); @@ -1710,7 +1732,7 @@ protected void processRequest(final Link link, final Request request) { } } } catch (final Throwable th) { - logger.warn("Caught: ", th); + logger.error("Caught: ", th); answer = new Answer(cmd, false, th.getMessage()); } answers[i] = answer; @@ -1725,7 +1747,7 @@ protected void processRequest(final Link link, final Request request) { 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); } } @@ -1753,7 +1775,6 @@ protected void doTask(final Task task) throws TaskExecutionException { } } catch (final UnsupportedVersionException e) { logger.warn(e.getMessage()); - // upgradeAgent(task.getLink(), data, e.getReason()); } catch (final ClassNotFoundException e) { final String message = String.format("Exception occurred when executing tasks! Error '%s'", e.getMessage()); logger.error(message); @@ -2113,7 +2134,7 @@ public ConfigKey[] getConfigKeys() { return new ConfigKey[] { CheckTxnBeforeSending, Workers, Port, Wait, AlertWait, DirectAgentLoadSize, DirectAgentPoolSize, DirectAgentThreadCap, EnableKVMAutoEnableDisable, ReadyCommandWait, GranularWaitTimeForCommands, RemoteAgentSslHandshakeTimeout, RemoteAgentMaxConcurrentNewConnections, - RemoteAgentNewConnectionsMonitorInterval }; + RemoteAgentNewConnectionsMonitorInterval, KVMHostDiscoverySshPort }; } protected class SetHostParamsListener implements Listener { @@ -2236,6 +2257,25 @@ public boolean transferDirectAgentsFromMS(String fromMsUuid, long fromMsId, long return true; } + @Override + public int getHostSshPort(HostVO host) { + if (host == null) { + return KVMHostDiscoverySshPort.value(); + } + + if (host.getHypervisorType() != HypervisorType.KVM) { + return Host.DEFAULT_SSH_PORT; + } + + _hostDao.loadDetails(host); + String hostPort = host.getDetail(Host.HOST_SSH_PORT); + if (StringUtils.isBlank(hostPort)) { + return KVMHostDiscoverySshPort.valueIn(host.getClusterId()); + } + + return Integer.parseInt(hostPort); + } + private GlobalLock getHostJoinLock(Long hostId) { return GlobalLock.getInternLock(String.format("%s-%s", "Host-Join", hostId)); } 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 c64489828033..38a198b73040 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 @@ -539,7 +539,7 @@ public SocketChannel connectToPeer(final String peerName, final SocketChannel pr ch1.close(); throw new IOException("SSL: Fail to init SSL! " + e); } - logger.debug("Connection to peer opened: {}, ip: {}", peerName, ip); + logger.debug("Connection to peer opened: {}, IP: {}", peerName, ip); _peers.put(peerName, ch1); _sslEngines.put(peerName, sslEngine); return ch1; @@ -551,7 +551,7 @@ public SocketChannel connectToPeer(final String peerName, final SocketChannel pr logger.error("failed to close failed peer socket: {}", ex); } } - logger.warn("Unable to connect to peer management server: {}, ip {} due to {}", peerName, ip, e.getMessage(), e); + logger.warn("Unable to connect to peer management server: {}, IP {} due to {}", peerName, ip, e.getMessage(), e); return null; } } @@ -965,7 +965,6 @@ protected void runInContext() { synchronized (_agentToTransferIds) { if (!_agentToTransferIds.isEmpty()) { logger.debug("Found {} agents to transfer", _agentToTransferIds.size()); - // for (Long hostId : _agentToTransferIds) { for (final Iterator iterator = _agentToTransferIds.iterator(); iterator.hasNext(); ) { final Long hostId = iterator.next(); final AgentAttache attache = findAttache(hostId); @@ -1307,11 +1306,20 @@ public String dispatch(final ClusterServicePdu pdu) { boolean result; try { - result = _resourceMgr.executeUserRequest(cmd.getHostId(), cmd.getEvent()); + result = _resourceMgr.executeUserRequest(cmd.getHostId(), cmd.getEvent(), cmd.isForced(), cmd.isForceDeleteStorage()); logger.debug("Result is {}", result); } catch (final AgentUnavailableException ex) { logger.warn("Agent is unavailable", ex); return null; + } catch (final RuntimeException ex) { + logger.error(String.format("Failed to execute propagated event %s for host %d", cmd.getEvent().name(), cmd.getHostId()), ex); + final Answer[] answers = new Answer[1]; + String details = ex.getMessage(); + if (details == null || details.isEmpty()) { + details = ex.toString(); + } + answers[0] = new Answer(cmd, false, details); + return _gson.toJson(answers); } final Answer[] answers = new Answer[1]; 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 3a6e1b622774..364db685c9de 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -17,6 +17,7 @@ package com.cloud.vm; +import static com.cloud.configuration.ConfigurationManagerImpl.EXPOSE_ERRORS_TO_USER; import static com.cloud.configuration.ConfigurationManagerImpl.MIGRATE_VM_ACROSS_CLUSTERS; import java.lang.reflect.Field; @@ -49,7 +50,9 @@ import javax.naming.ConfigurationException; import javax.persistence.EntityExistsException; - +import com.cloud.agent.api.PostMigrationCommand; +import com.cloud.storage.clvm.ClvmPoolManager; +import com.cloud.hypervisor.KVMGuru; import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; @@ -71,6 +74,7 @@ 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.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.framework.ca.Certificate; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; @@ -134,6 +138,7 @@ import com.cloud.agent.api.PrepareExternalProvisioningCommand; import com.cloud.agent.api.PrepareForMigrationAnswer; import com.cloud.agent.api.PrepareForMigrationCommand; +import com.cloud.agent.api.PreMigrationCommand; import com.cloud.agent.api.RebootAnswer; import com.cloud.agent.api.RebootCommand; import com.cloud.agent.api.RecreateCheckpointsCommand; @@ -150,11 +155,15 @@ import com.cloud.agent.api.StopCommand; import com.cloud.agent.api.UnPlugNicAnswer; import com.cloud.agent.api.UnPlugNicCommand; +import com.cloud.agent.api.UnmanageInstanceCommand; import com.cloud.agent.api.UnregisterVMCommand; +import com.cloud.agent.api.UpdateVmNicAnswer; +import com.cloud.agent.api.UpdateVmNicCommand; import com.cloud.agent.api.VmDiskStatsEntry; import com.cloud.agent.api.VmNetworkStatsEntry; import com.cloud.agent.api.VmStatsEntry; import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.DiskTO; import com.cloud.agent.api.to.DpdkTO; import com.cloud.agent.api.to.GPUDeviceTO; @@ -261,6 +270,7 @@ import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplateZoneDao; import com.cloud.storage.dao.VolumeDao; +import com.cloud.storage.dao.VolumeDetailsDao; import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.Account; @@ -297,14 +307,13 @@ import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.UserVmDao; -import com.cloud.vm.dao.VMInstanceDetailsDao; import com.cloud.vm.dao.VMInstanceDao; +import com.cloud.vm.dao.VMInstanceDetailsDao; 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(); @@ -356,6 +365,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Inject private VolumeDao _volsDao; @Inject + private VolumeDetailsDao _volsDetailsDao; + @Inject private HighAvailabilityManager _haMgr; @Inject private HostPodDao _podDao; @@ -458,6 +469,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac ExtensionsManager extensionsManager; @Inject ExtensionDetailsDao extensionDetailsDao; + @Inject + ClvmPoolManager clvmPoolManager; VmWorkJobHandlerProxy _jobHandlerProxy = new VmWorkJobHandlerProxy(this); @@ -486,7 +499,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac static final ConfigKey ClusterVMMetaDataSyncInterval = new ConfigKey("Advanced", Integer.class, "vmmetadata.sync.interval", "180", "Cluster VM metadata sync interval in seconds", false); - static final ConfigKey VmJobCheckInterval = new ConfigKey("Advanced", + public static final ConfigKey VmJobCheckInterval = new ConfigKey("Advanced", Long.class, "vm.job.check.interval", "3000", "Interval in milliseconds to check if the job is complete", false); static final ConfigKey VmJobTimeout = new ConfigKey("Advanced", @@ -536,7 +549,7 @@ public void allocate(final String vmInstanceName, final VirtualMachineTemplate t 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()); + logger.info("Allocating Instance from Template: {} with hostname: {} and {} networks", template, vmInstanceName, auxiliaryNetworks.size()); VMInstanceVO persistedVm = null; try { final VMInstanceVO vm = _vmDao.findVMByInstanceName(vmInstanceName); @@ -559,7 +572,7 @@ public void allocate(final String vmInstanceName, final VirtualMachineTemplate t } final Long rootDiskSizeFinal = rootDiskSize; - logger.debug("Allocating nics for {}", persistedVm); + logger.debug("Allocating NICs for {}", persistedVm); try { if (!vmProfile.getBootArgs().contains("ExternalLoadBalancerVm")) { @@ -571,7 +584,13 @@ public void allocate(final String vmInstanceName, final VirtualMachineTemplate t logger.debug("Allocating disks for {}", persistedVm); - allocateRootVolume(persistedVm, template, rootDiskOfferingInfo, owner, rootDiskSizeFinal, volume, snapshot); + if (isBlankInstance(template)) { + logger.debug("Template is a dummy template for hypervisor {}, skipping volume allocation", hyperType); + return; + } else { + 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); @@ -582,7 +601,7 @@ public void allocate(final String vmInstanceName, final VirtualMachineTemplate t 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); + dataDiskOfferingInfo.getMinIops(), dataDiskOfferingInfo.getMaxIops(), persistedVm, template, owner, deviceId, dataDiskOfferingInfo.getKmsKeyId(), true); } } if (datadiskTemplateToDiskOfferingMap != null && !datadiskTemplateToDiskOfferingMap.isEmpty()) { @@ -592,7 +611,7 @@ public void allocate(final String vmInstanceName, final VirtualMachineTemplate t 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, diskNumber); + persistedVm, dataDiskTemplate, owner, diskNumber, null, true); diskNumber++; } } @@ -622,12 +641,12 @@ private void allocateRootVolume(VMInstanceVO vm, VirtualMachineTemplate template String rootVolumeName = String.format("ROOT-%s", vm.getId()); if (template.getFormat() == ImageFormat.ISO) { volumeMgr.allocateRawVolume(Type.ROOT, rootVolumeName, rootDiskOfferingInfo.getDiskOffering(), rootDiskOfferingInfo.getSize(), - rootDiskOfferingInfo.getMinIops(), rootDiskOfferingInfo.getMaxIops(), vm, template, owner, null); + rootDiskOfferingInfo.getMinIops(), rootDiskOfferingInfo.getMaxIops(), vm, template, owner, null, rootDiskOfferingInfo.getKmsKeyId(), true); } 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, volume, snapshot); + rootDiskOfferingInfo.getMinIops(), rootDiskOfferingInfo.getMaxIops(), template, vm, owner, rootDiskOfferingInfo.getKmsKeyId(), volume, snapshot); } } finally { // Remove volumeContext and pop vmContext back @@ -928,11 +947,27 @@ public void start(final String vmUuid, final Map params, final DeploymentPlan planToDeploy, final DeploymentPlanner planner) { try { advanceStart(vmUuid, params, planToDeploy, planner); - } catch (ConcurrentOperationException | InsufficientCapacityException e) { - throw new CloudRuntimeException(String.format("Unable to start a VM [%s] due to [%s].", vmUuid, e.getMessage()), e).add(VirtualMachine.class, vmUuid); + } catch (ConcurrentOperationException e) { + final CallContext cctxt = CallContext.current(); + final Account account = cctxt.getCallingAccount(); + if (canExposeError(account)) { + throw new CloudRuntimeException(String.format("Unable to start a VM [%s] due to [%s].", vmUuid, e.getMessage()), e).add(VirtualMachine.class, vmUuid); + } + throw new CloudRuntimeException(String.format("Unable to start a VM [%s] due to concurrent operation.", vmUuid), e).add(VirtualMachine.class, vmUuid); + } catch (final InsufficientCapacityException e) { + final CallContext cctxt = CallContext.current(); + final Account account = cctxt.getCallingAccount(); + if (canExposeError(account)) { + throw new CloudRuntimeException(String.format("Unable to start a VM [%s] due to [%s].", vmUuid, e.getMessage()), e).add(VirtualMachine.class, vmUuid); + } + throw new CloudRuntimeException(String.format("Unable to start a VM [%s] due to insufficient capacity.", vmUuid), e).add(VirtualMachine.class, vmUuid); } catch (final ResourceUnavailableException e) { - if (e.getScope() != null && e.getScope().equals(VirtualRouter.class)){ - throw new CloudRuntimeException("Network is unavailable. Please contact administrator", e).add(VirtualMachine.class, vmUuid); + if (e.getScope() != null && e.getScope().equals(VirtualRouter.class)) { + Account callingAccount = CallContext.current().getCallingAccount(); + String errorSuffix = (callingAccount != null && callingAccount.getType() == Account.Type.ADMIN) ? + String.format("Failure: %s", e.getMessage()) : + "Please contact administrator."; + throw new CloudRuntimeException(String.format("The Network for VM %s is unavailable. %s", vmUuid, errorSuffix), e).add(VirtualMachine.class, vmUuid); } throw new CloudRuntimeException(String.format("Unable to start a VM [%s] due to [%s].", vmUuid, e.getMessage()), e).add(VirtualMachine.class, vmUuid); } @@ -994,7 +1029,7 @@ public Ternary doInTransaction(final if (stateTransitTo(vm, Event.StartRequested, null, work.getId())) { logger.debug("Successfully transitioned to start state for {} reservation id = {}", vm, work.getId()); if (VirtualMachine.Type.User.equals(vm.type) && ResourceCountRunningVMsonly.value()) { - _resourceLimitMgr.incrementVmResourceCount(owner.getAccountId(), vm.isDisplay(), offering, template); + _resourceLimitMgr.incrementVmResourceCount(owner.getAccountId(), vm.isDisplay(), offering, template, null); } return new Ternary<>(vm, context, work); } @@ -1020,7 +1055,7 @@ public Ternary doInTransaction(final final State state = instance.getState(); if (state == State.Running) { - logger.debug("VM is already started: " + vm); + logger.debug("Instance is already started: " + vm); return null; } @@ -1358,8 +1393,10 @@ public void orchestrateStart(final String vmUuid, final Map vols = _volsDao.findReadyRootVolumesByInstance(vm.getId()); for (final VolumeVO vol : vols) { final Long volTemplateId = vol.getTemplateId(); if (volTemplateId != null && volTemplateId != template.getId()) { - logger.debug("{} of {} is READY, but template ids don't match, let the planner reassign a new pool", vol, vm); + logger.debug("{} of {} is READY, but Template IDs don't match, let the planner reassign a new pool", vol, vm); continue; } @@ -1447,8 +1485,13 @@ public void orchestrateStart(final String vmUuid, final Map unmanage(String vmUuid, Long paramHostId) { VMInstanceVO vm = _vmDao.findByUuid(vmUuid); if (vm == null || vm.getRemoved() != null) { throw new CloudRuntimeException("Could not find VM with id = " + vmUuid); @@ -2014,6 +2096,10 @@ public boolean unmanage(String vmUuid) { throw new ConcurrentOperationException(msg); } + Long agentHostId = vm.getHostId(); + if (HypervisorType.KVM.equals(vm.getHypervisorType())) { + agentHostId = persistDomainForKVM(vm, paramHostId); + } Boolean result = Transaction.execute(new TransactionCallback() { @Override public Boolean doInTransaction(TransactionStatus status) { @@ -2037,21 +2123,66 @@ public Boolean doInTransaction(TransactionStatus status) { return true; } }); + HostVO host = ApiDBUtils.findHostById(agentHostId); + if (host == null) { + return new Pair<>(result, null); + } + logger.debug("Selected host UUID: {} to unmanage Instance: {}.", host.getUuid(), vm.getName()); + ActionEventUtils.onActionEvent(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM, Domain.ROOT_DOMAIN, EventTypes.EVENT_VM_UNMANAGE, + String.format("Successfully unmanaged Instance: %s (ID: %s) on host ID: %s", vm.getName(), vm.getUuid(), host.getUuid()), + vm.getId(), ApiCommandResourceType.VirtualMachine.toString()); + return new Pair<>(result, host.getUuid()); + } + + Long persistDomainForKVM(VMInstanceVO vm, Long paramHostId) { + Long agentHostId = vm.getHostId(); + String vmName = vm.getName(); + UnmanageInstanceCommand unmanageInstanceCommand; + if (State.Stopped.equals(vm.getState())) { + if (paramHostId == null) { + Pair clusterAndHostId = findClusterAndHostIdForVm(vm, false); + agentHostId = clusterAndHostId.second(); + if (agentHostId == null) { + String errorMsg = "No available host to persist domain XML for Instance: " + vmName; + logger.debug(errorMsg); + throw new CloudRuntimeException(errorMsg); + } + } else { + agentHostId = paramHostId; + } + unmanageInstanceCommand = new UnmanageInstanceCommand(prepVmSpecForUnmanageCmd(vm.getId(), agentHostId)); // reconstruct vmSpec for stopped instance + } else { + unmanageInstanceCommand = new UnmanageInstanceCommand(vmName); + unmanageInstanceCommand.setConfigDriveAttached(vmInstanceDetailsDao.findDetail(vm.getId(), VmDetailConstants.CONFIG_DRIVE_LOCATION) != null); + } - return BooleanUtils.isTrue(result); + logger.debug("Selected host ID: {} to persist domain XML for Instance: {}.", agentHostId, vmName); + try { + Answer answer = _agentMgr.send(agentHostId, unmanageInstanceCommand); + if (!answer.getResult()) { + String errorMsg = "Failed to persist domain XML for Instance: " + vmName + " on host ID: " + agentHostId; + logger.debug(errorMsg); + throw new CloudRuntimeException(errorMsg); + } + } catch (AgentUnavailableException | OperationTimedoutException e) { + String errorMsg = "Failed to send command to persist domain XML for Instance: " + vmName + " on host ID: " + agentHostId; + logger.error(errorMsg, e); + throw new CloudRuntimeException(errorMsg); + } + return agentHostId; } /** * Clean up VM snapshots (if any) from DB */ - private void unmanageVMSnapshots(VMInstanceVO vm) { + void unmanageVMSnapshots(VMInstanceVO vm) { _vmSnapshotMgr.deleteVMSnapshotsFromDB(vm.getId(), true); } /** * Clean up volumes for a VM to be unmanaged from CloudStack */ - private void unmanageVMVolumes(VMInstanceVO vm) { + void unmanageVMVolumes(VMInstanceVO vm) { final Long hostId = vm.getHostId() != null ? vm.getHostId() : vm.getLastHostId(); if (hostId != null) { volumeMgr.revokeAccess(vm.getId(), hostId); @@ -2069,7 +2200,7 @@ private void unmanageVMVolumes(VMInstanceVO vm) { * - If 'unmanage.vm.preserve.nics' = true: then the NICs are not removed but still Allocated, to preserve MAC addresses * - If 'unmanage.vm.preserve.nics' = false: then the NICs are removed while unmanaging */ - private void unmanageVMNics(VirtualMachineProfile profile, VMInstanceVO vm) { + void unmanageVMNics(VirtualMachineProfile profile, VMInstanceVO vm) { logger.debug("Cleaning up NICs of {}.", vm.toString()); Boolean preserveNics = UnmanagedVMsManager.UnmanageVMPreserveNic.valueIn(vm.getDataCenterId()); if (BooleanUtils.isTrue(preserveNics)) { @@ -2109,7 +2240,6 @@ private List> getVolumesToDisconnect(VirtualMachine vm) { 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)) { @@ -2461,7 +2591,7 @@ private void advanceStop(final VMInstanceVO vm, final boolean cleanUpEvenIfUnabl } stopped = answer.getResult(); if (!stopped) { - throw new CloudRuntimeException("Unable to stop the virtual machine due to " + answer.getDetails()); + throw new CloudRuntimeException("Unable to stop the Instance due to " + answer.getDetails()); } vmGuru.finalizeStop(profile, answer); final GPUDeviceTO gpuDevice = stop.getGpuDevice(); @@ -2524,7 +2654,7 @@ public Boolean doInTransaction(TransactionStatus status) throws NoTransitionExce if (result && VirtualMachine.Type.User.equals(vm.type) && ResourceCountRunningVMsonly.value()) { ServiceOfferingVO offering = _offeringDao.findById(vm.getId(), vm.getServiceOfferingId()); VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); - _resourceLimitMgr.decrementVmResourceCount(vm.getAccountId(), vm.isDisplay(), offering, template); + _resourceLimitMgr.decrementVmResourceCount(vm.getAccountId(), vm.isDisplay(), offering, template, null); } return result; } @@ -2625,8 +2755,8 @@ public void doInTransactionWithoutResult(final TransactionStatus status) throws private void deleteVMSnapshots(VMInstanceVO vm, boolean expunge) { if (! vm.getHypervisorType().equals(HypervisorType.VMware)) { if (!_vmSnapshotMgr.deleteAllVMSnapshots(vm.getId(), null)) { - logger.debug("Unable to delete all snapshots for {}", vm); - throw new CloudRuntimeException("Unable to delete vm snapshots for " + vm); + logger.debug("Unable to delete all Snapshots for {}", vm); + throw new CloudRuntimeException("Unable to delete Instance Snapshots for " + vm); } } else { @@ -2655,7 +2785,7 @@ protected boolean checkVmOnHost(final VirtualMachine vm, final long hostId) thro if (command != null) { RestoreVMSnapshotAnswer restoreVMSnapshotAnswer = (RestoreVMSnapshotAnswer) _agentMgr.send(hostId, command); if (restoreVMSnapshotAnswer == null || !restoreVMSnapshotAnswer.getResult()) { - logger.warn("Unable to restore the vm snapshot from image file after live migration of vm with vmsnapshots: {}", restoreVMSnapshotAnswer == null ? "null answer" : restoreVMSnapshotAnswer.getDetails()); + logger.warn("Unable to restore the Instance Snapshot from image file after live migration of Instance with vmsnapshots: {}", restoreVMSnapshotAnswer == null ? "null answer" : restoreVMSnapshotAnswer.getDetails()); } } } @@ -2792,6 +2922,7 @@ private void markVolumesInPool(VMInstanceVO vm, Answer[] hypervisorMigrationResu } volume.setPath(result.getPath()); volume.setPoolId(pool.getId()); + volume.setPoolType(pool.getPoolType()); if (result.getChainInfo() != null) { volume.setChainInfo(result.getChainInfo()); } @@ -2841,7 +2972,7 @@ private Map prepareVmStorageMigration(VMInstanceVO vm, Map< clusterId = cluster.getId(); } if (dataCenterId == null) { - String msg = "Unable to migrate vm: failed to create deployment destination with given volume to pool map"; + String msg = "Unable to migrate Instance: failed to create deployment destination with given volume to pool map"; logger.debug(msg); throw new CloudRuntimeException(msg); } @@ -2852,7 +2983,7 @@ private Map prepareVmStorageMigration(VMInstanceVO vm, Map< try { stateTransitTo(vm, Event.StorageMigrationRequested, null); } catch (final NoTransitionException e) { - String msg = String.format("Unable to migrate vm: %s", vm.getUuid()); + String msg = String.format("Unable to migrate Instance: %s", vm.getUuid()); logger.warn(msg, e); throw new CloudRuntimeException(msg, e); } @@ -2978,14 +3109,14 @@ public void migrate(final String vmUuid, final long srcHostId, final DeployDesti private void orchestrateMigrate(final String vmUuid, final long srcHostId, final DeployDestination dest) throws ResourceUnavailableException, ConcurrentOperationException { final VMInstanceVO vm = _vmDao.findByUuid(vmUuid); if (vm == null) { - logger.debug("Unable to find the vm {}", vmUuid); - throw new CloudRuntimeException("Unable to find a virtual machine with id " + vmUuid); + logger.debug("Unable to find the Instance {}", vmUuid); + throw new CloudRuntimeException("Unable to find a Instance with ID: " + vmUuid); } migrate(vm, srcHostId, dest); } protected void migrate(final VMInstanceVO vm, final long srcHostId, final DeployDestination dest) throws ResourceUnavailableException, ConcurrentOperationException { - logger.info("Migrating {} to {}", vm, dest); + logger.info("Start preparing migration of the VM: {} to {}", vm, dest); final long dstHostId = dest.getHost().getId(); final Host fromHost = _hostDao.findById(srcHostId); if (fromHost == null) { @@ -3035,6 +3166,9 @@ protected void migrate(final VMInstanceVO vm, final long srcHostId, final Deploy updateOverCommitRatioForVmProfile(profile, dest.getHost().getClusterId()); final VirtualMachineTO to = toVmTO(profile); + + executePreMigrationCommand(vm, to, srcHostId); + final PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand(to); setVmNetworkDetails(vm, to); @@ -3050,9 +3184,11 @@ protected void migrate(final VMInstanceVO vm, final long srcHostId, final Deploy if (pfma == null || !pfma.getResult()) { final String details = pfma != null ? pfma.getDetails() : "null answer returned"; final String msg = "Unable to prepare for migration due to " + details; + logger.error("Failed to prepare destination host {} for migration of VM {} : {}", dstHostId, vm.getInstanceName(), details); pfma = null; throw new AgentUnavailableException(msg, dstHostId); } + logger.debug("Successfully prepared destination host {} for migration of VM {} ", dstHostId, vm.getInstanceName()); } catch (final OperationTimedoutException e1) { throw new AgentUnavailableException("Operation timed out", dstHostId); } finally { @@ -3073,18 +3209,23 @@ protected void migrate(final VMInstanceVO vm, final long srcHostId, final Deploy volumeMgr.release(vm.getId(), dstHostId); } - logger.info("Migration cancelled because state has changed: {}", vm); - throw new ConcurrentOperationException("Migration cancelled because state has changed: " + vm); + String msg = "Migration cancelled because state has changed: " + vm; + logger.warn(msg); + throw new ConcurrentOperationException(msg); } } catch (final NoTransitionException e1) { _networkMgr.rollbackNicForMigration(vmSrc, profile); volumeMgr.release(vm.getId(), dstHostId); - logger.info("Migration cancelled because {}", e1.getMessage()); + String msg = String.format("Migration cancelled for VM %s due to state transition failure: %s", + vm.getInstanceName(), e1.getMessage()); + logger.warn(msg, e1); throw new ConcurrentOperationException("Migration cancelled because " + e1.getMessage()); } catch (final CloudRuntimeException e2) { _networkMgr.rollbackNicForMigration(vmSrc, profile); volumeMgr.release(vm.getId(), dstHostId); - logger.info("Migration cancelled because {}", e2.getMessage()); + String msg = String.format("Migration cancelled for VM %s due to runtime exception: %s", + vm.getInstanceName(), e2.getMessage()); + logger.error(msg, e2); work.setStep(Step.Done); _workDao.update(work.getId(), work); try { @@ -3104,8 +3245,12 @@ protected void migrate(final VMInstanceVO vm, final long srcHostId, final Deploy final Answer ma = _agentMgr.send(vm.getLastHostId(), mc); if (ma == null || !ma.getResult()) { final String details = ma != null ? ma.getDetails() : "null answer returned"; + String msg = String.format("Migration command failed for VM %s on source host id=%s to destination host %s: %s", + vm.getInstanceName(), vm.getLastHostId(), dstHostId, details); + logger.error(msg); throw new CloudRuntimeException(details); } + logger.info("Migration command successful for VM {}", vm.getInstanceName()); } catch (final OperationTimedoutException e) { boolean success = false; if (HypervisorType.KVM.equals(vm.getHypervisorType())) { @@ -3142,7 +3287,7 @@ protected void migrate(final VMInstanceVO vm, final long srcHostId, final Deploy try { if (!checkVmOnHost(vm, dstHostId)) { - logger.error("Unable to complete migration for {}", vm); + logger.error("Migration verification failed for VM {} : VM not found on destination host {} ", vm.getInstanceName(), dstHostId); try { _agentMgr.send(srcHostId, new Commands(cleanup(vm, dpdkInterfaceMapping)), null); } catch (final AgentUnavailableException e) { @@ -3155,9 +3300,10 @@ protected void migrate(final VMInstanceVO vm, final long srcHostId, final Deploy logger.warn("Error while checking the vm {} on host {}", vm, dest.getHost(), e); } migrated = true; + executePostMigrationCommand(vm, to, dstHostId); } finally { if (!migrated) { - logger.info("Migration was unsuccessful. Cleaning up: {}", vm); + 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 @@ -3169,7 +3315,7 @@ protected void migrate(final VMInstanceVO vm, final long srcHostId, final Deploy try { _agentMgr.send(dstHostId, new Commands(cleanup(vm, dpdkInterfaceMapping)), null); } catch (final AgentUnavailableException ae) { - logger.warn("Looks like the destination Host is unavailable for cleanup", ae); + logger.warn("Destination host {} unavailable for cleanup after failed migration of VM {}", dstHostId, vm.getInstanceName(), ae); } _networkMgr.setHypervisorHostname(profile, dest, false); try { @@ -3178,6 +3324,7 @@ protected void migrate(final VMInstanceVO vm, final long srcHostId, final Deploy logger.warn(e.getMessage()); } } else { + logger.info("Migration completed successfully for VM %s" + vm); _networkMgr.commitNicForMigration(vmSrc, profile); volumeMgr.release(vm.getId(), srcHostId); // deallocate GPU devices for the VM on the src host after migration is complete @@ -3193,6 +3340,30 @@ protected void migrate(final VMInstanceVO vm, final long srcHostId, final Deploy } } + private void executePostMigrationCommand(VMInstanceVO vm, VirtualMachineTO to, long dstHostId) { + if (!(vm.getHypervisorType() == HypervisorType.KVM && hasClvmVolumes(vm.getId()))) { + return; + } + final String dstHostUuid = _hostDao.findById(dstHostId).getUuid(); + try { + logger.info("Executing post-migration tasks for VM {} with CLVM volumes on destination host {}", vm.getInstanceName(), dstHostUuid); + final PostMigrationCommand postMigrationCommand = new PostMigrationCommand(to, vm.getInstanceName()); + final Answer postMigrationAnswer = _agentMgr.send(dstHostId, postMigrationCommand); + + if (postMigrationAnswer == null || !postMigrationAnswer.getResult()) { + final String details = postMigrationAnswer != null ? postMigrationAnswer.getDetails() : "null answer returned"; + logger.warn("Post-migration tasks failed for VM {} on destination host {}: {}. Migration completed but some cleanup may be needed.", + vm.getInstanceName(), dstHostUuid, details); + } else { + logger.info("Successfully completed post-migration tasks for VM {} on destination host {}", vm.getInstanceName(), dstHostUuid); + } + } catch (Exception e) { + logger.warn("Exception during post-migration tasks for VM {} on destination host {}: {}. Migration completed but some cleanup may be needed.", + vm.getInstanceName(), dstHostUuid, e.getMessage(), e); + } + updateClvmLockHostForVmVolumes(vm.getId(), dstHostId); + } + /** * Create and set parameters for the {@link MigrateCommand} used in the migration and scaling of VMs. */ @@ -3208,6 +3379,7 @@ protected MigrateCommand buildMigrateCommand(VMInstanceVO vmInstance, VirtualMac migrateCommand.setVlanToPersistenceMap(vlanToPersistenceMap); } + logger.debug("Setting auto convergence to: {}", StorageManager.KvmAutoConvergence.value()); migrateCommand.setAutoConvergence(StorageManager.KvmAutoConvergence.value()); migrateCommand.setHostGuid(destination.getHost().getGuid()); @@ -3238,6 +3410,27 @@ private void updateVmPod(VMInstanceVO vm, long dstHostId) { _vmDao.persist(newVm); } + /** + * Updates CLVM_LOCK_HOST_ID for all CLVM volumes attached to a VM after VM migration. + * This ensures that subsequent operations on CLVM volumes are routed to the correct host. + * + * @param vmId The ID of the VM that was migrated + * @param destHostId The destination host ID where the VM now resides + */ + private void updateClvmLockHostForVmVolumes(long vmId, long destHostId) { + List volumes = _volsDao.findByInstance(vmId); + if (CollectionUtils.isEmpty(volumes)) { + return; + } + + for (VolumeVO volume : volumes) { + StoragePoolVO pool = _storagePoolDao.findById(volume.getPoolId()); + if (pool != null && ClvmPoolManager.isClvmPoolType(pool.getPoolType())) { + clvmPoolManager.setClvmLockHostId(volume.getId(), destHostId); + } + } + } + /** * We create the mapping of volumes and storage pool to migrate the VMs according to the information sent by the user. * If the user did not enter a complete mapping, the volumes that were left behind will be auto mapped using {@link #createStoragePoolMappingsForVolumes(VirtualMachineProfile, DataCenterDeployment, Map, List)} @@ -3358,7 +3551,8 @@ protected void createStoragePoolMappingsForVolumes(VirtualMachineProfile profile protected boolean shouldMapVolume(VirtualMachineProfile profile, StoragePoolVO currentPool) { boolean isManaged = currentPool.isManaged(); boolean isNotKvm = HypervisorType.KVM != profile.getHypervisorType(); - return isNotKvm || isManaged; + boolean isClvm = ClvmPoolManager.isClvmPoolType(currentPool.getPoolType()); + return isNotKvm || isManaged || isClvm; } /** @@ -3896,19 +4090,6 @@ protected void runInContext() { } } - @Override - public boolean isVirtualMachineUpgradable(final VirtualMachine vm, final ServiceOffering offering) { - boolean isMachineUpgradable = true; - for (final HostAllocator allocator : hostAllocators) { - isMachineUpgradable = allocator.isVirtualMachineUpgradable(vm, offering); - if (!isMachineUpgradable) { - break; - } - } - - return isMachineUpgradable; - } - @Override public void reboot(final String vmUuid, final Map params) throws InsufficientCapacityException, ResourceUnavailableException { try { @@ -3950,8 +4131,8 @@ private void orchestrateReboot(final String vmUuid, final Map nics = _nicsDao.listByVmId(vmProfile.getId()); + Collections.sort(nics, (nic1, nic2) -> { + Long nicId1 = Long.valueOf(nic1.getDeviceId()); + Long nicId2 = Long.valueOf(nic2.getDeviceId()); + return nicId1.compareTo(nicId2); + }); + + for (final NicVO nic : nics) { + final Network network = _networkModel.getNetwork(nic.getNetworkId()); + final NicProfile nicProfile = + new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), null, _networkModel.isSecurityGroupSupportedInNetwork(network), + _networkModel.getNetworkTag(vmProfile.getHypervisorType(), network)); + vmProfile.addNic(nicProfile); + } + + List volumes = _volsDao.findUsableVolumesForInstance(vmId); + for (VolumeVO vol: volumes) { + VolumeInfo volumeInfo = volumeDataFactory.getVolume(vol.getId()); + DataTO dataTO = volumeInfo.getTO(); + DiskTO disk = storageMgr.getDiskWithThrottling(dataTO, vol.getVolumeType(), vol.getDeviceId(), vol.getPath(), vm.getServiceOfferingId(), vol.getDiskOfferingId()); + vmProfile.addDisk(disk); + } + + Map details = vmInstanceDetailsDao.listDetailsKeyPairs(vmId, + List.of(VirtualMachineProfile.Param.BootType.getName(), VirtualMachineProfile.Param.BootMode.getName(), + VirtualMachineProfile.Param.UefiFlag.getName())); + + if (details.containsKey(VirtualMachineProfile.Param.BootType.getName())) { + vmProfile.getParameters().put(VirtualMachineProfile.Param.BootType, details.get(VirtualMachineProfile.Param.BootType.getName())); + } + + if (details.containsKey(VirtualMachineProfile.Param.BootMode.getName())) { + vmProfile.getParameters().put(VirtualMachineProfile.Param.BootMode, details.get(VirtualMachineProfile.Param.BootMode.getName())); + } + + if (details.containsKey(VirtualMachineProfile.Param.UefiFlag.getName())) { + vmProfile.getParameters().put(VirtualMachineProfile.Param.UefiFlag, details.get(VirtualMachineProfile.Param.UefiFlag.getName())); + } + + return toVmTO(vmProfile); + } + protected VirtualMachineTO getVmTO(Long vmId) { final VMInstanceVO vm = _vmDao.findById(vmId); final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); @@ -4292,11 +4529,6 @@ public void checkIfCanUpgrade(final VirtualMachine vmInstance, final ServiceOffe throw new InvalidParameterValueException("isSystem property is different for current service offering and new service offering"); } - if (!isVirtualMachineUpgradable(vmInstance, newServiceOffering)) { - throw new InvalidParameterValueException("Unable to upgrade virtual machine, not enough resources available " + "for an offering of " + - newServiceOffering.getCpu() + " cpu(s) at " + newServiceOffering.getSpeed() + " Mhz, and " + newServiceOffering.getRamSize() + " MB of memory"); - } - final List currentTags = StringUtils.csvTagsToList(currentDiskOffering.getTags()); final List newTags = StringUtils.csvTagsToList(newDiskOffering.getTags()); if (VolumeApiServiceImpl.MatchStoragePoolTagsWithDiskOffering.valueIn(vmInstance.getDataCenterId())) { @@ -4410,7 +4642,7 @@ private NicProfile orchestrateAddVmToNetwork(final VirtualMachine vm, final Netw final CallContext cctx = CallContext.current(); checkIfNetworkExistsForUserVM(vm, network); - logger.debug("Adding vm {} to network {}; requested nic profile {}", vm, network, requested); + logger.debug("Adding Instance {} to Network {}; requested NIC profile {}", vm, network, requested); final VMInstanceVO vmVO = _vmDao.findById(vm.getId()); final ReservationContext context = new ReservationContextImpl(null, null, cctx.getCallingUser(), cctx.getCallingAccount()); @@ -4429,7 +4661,7 @@ private NicProfile orchestrateAddVmToNetwork(final VirtualMachine vm, final Netw final NicTO nicTO = toNicTO(nic, vmProfile.getVirtualMachine().getHypervisorType()); //4) plug the nic to the vm - logger.debug("Plugging nic for vm {} in network {}", vm, network); + logger.debug("Plugging NIC for Instance {} in Network {}", vm, network); boolean result = false; try { @@ -4445,12 +4677,12 @@ private NicProfile orchestrateAddVmToNetwork(final VirtualMachine vm, final Netw } return nic; } else { - logger.warn("Failed to plug nic to the vm {} in network {}", vm, network); + logger.warn("Failed to plug NIC to the Instance {} in Network {}", vm, network); return null; } } finally { if (!result) { - logger.debug("Removing nic {} from vm {} as nic plug failed on the backend.", nic, vmProfile.getVirtualMachine()); + logger.debug("Removing NIC {} from Instance {} as NIC plug failed on the backend.", nic, vmProfile.getVirtualMachine()); _networkMgr.removeNic(vmProfile, _nicsDao.findById(nic.getId())); } } @@ -4521,25 +4753,25 @@ private boolean orchestrateRemoveNicFromVm(final VirtualMachine vm, final Nic ni if (vm.getState() == State.Running) { final NicTO nicTO = toNicTO(nicProfile, vmProfile.getVirtualMachine().getHypervisorType()); - logger.debug("Un-plugging nic {} for vm {} from network {}.", nic, vm, network); + logger.debug("Un-plugging NIC {} for Instance {} from Network {}.", nic, vm, network); final boolean result = unplugNic(network, nicTO, vmTO, context, dest); if (result) { _userVmMgr.setupVmForPvlan(false, vm.getHostId(), nicProfile); - logger.debug("Nic is unplugged successfully for vm {} in network {}.", vm, network); + logger.debug("NIC is unplugged successfully for Instance {} in Network {}.", vm, network); final long isDefault = nic.isDefaultNic() ? 1 : 0; UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_REMOVE, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), Long.toString(nic.getId()), network.getNetworkOfferingId(), null, isDefault, VirtualMachine.class.getName(), vm.getUuid(), vm.isDisplay()); } else { - logger.warn("Failed to unplug nic for the vm {} from network {}.", vm, network); + logger.warn("Failed to unplug NIC for the Instance {} from Network {}.", vm, network); return false; } } else if (vm.getState() != State.Stopped) { - logger.warn("Unable to remove vm {} from network {}", vm, network); - throw new ResourceUnavailableException("Unable to remove vm " + vm + " from network, is not in the right state", DataCenter.class, vm.getDataCenterId()); + logger.warn("Unable to remove Instance {} from Network {}", vm, network); + throw new ResourceUnavailableException("Unable to remove Instance " + vm + " from Network, is not in the right state", DataCenter.class, vm.getDataCenterId()); } _networkMgr.releaseNic(vmProfile, nic); - logger.debug("Successfully released nic {} for vm {}", nic, vm); + logger.debug("Successfully released NIC {} for Instance {}", nic, vm); _networkMgr.removeNic(vmProfile, nic); _nicsDao.remove(nic.getId()); @@ -4574,13 +4806,13 @@ private boolean orchestrateRemoveVmFromNetwork(final VirtualMachine vm, final Ne } if (nic == null) { - logger.warn("Could not get a nic with {}", network); + logger.warn("Could not get a NIC with {}", network); return false; } if (nic.isDefaultNic() && vm.getType() == VirtualMachine.Type.User) { - logger.warn("Failed to remove nic from {} in {}, nic is default.", vm, network); - throw new CloudRuntimeException("Failed to remove nic from " + vm + " in " + network + ", nic is default."); + logger.warn("Failed to remove NIC from {} in {}, NIC is default.", vm, network); + throw new CloudRuntimeException("Failed to remove NIC from " + vm + " in " + network + ", NIC is default."); } final Nic lock = _nicsDao.acquireInLockTable(nic.getId()); @@ -4756,6 +4988,12 @@ private void orchestrateMigrateForScale(final String vmUuid, final long srcHostI volumeMgr.prepareForMigration(profile, dest); final VirtualMachineTO to = toVmTO(profile); + + // Step 1: Send PreMigrationCommand to source host to convert CLVM volumes to shared mode + // This must happen BEFORE PrepareForMigrationCommand on destination to avoid lock conflicts + executePreMigrationCommand(vm, to, srcHostId); + + // Step 2: Send PrepareForMigrationCommand to destination host final PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand(to); ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Migrating, vm.getType(), vm.getId()); @@ -4840,6 +5078,7 @@ private void orchestrateMigrateForScale(final String vmUuid, final long srcHostI } migrated = true; + executePostMigrationCommand(vm, to, dstHostId); } finally { if (!migrated) { logger.info("Migration was unsuccessful. Cleaning up: {}", vm); @@ -5003,7 +5242,7 @@ public VMInstanceVO reConfigureVm(final String vmUuid, final ServiceOffering old try { result = retrieveResultFromJobOutcomeAndThrowExceptionIfNeeded(outcome); } catch (Exception ex) { - throw new RuntimeException("Unhandled exception", ex); + throw new RuntimeException("Unable to reconfigure VM.", ex); } if (result != null) { @@ -5016,22 +5255,29 @@ public VMInstanceVO reConfigureVm(final String vmUuid, final ServiceOffering old private VMInstanceVO orchestrateReConfigureVm(String vmUuid, ServiceOffering oldServiceOffering, ServiceOffering newServiceOffering, boolean reconfiguringOnExistingHost) throws ResourceUnavailableException, ConcurrentOperationException { - final VMInstanceVO vm = _vmDao.findByUuid(vmUuid); + VMInstanceVO vm = _vmDao.findByUuid(vmUuid); HostVO hostVo = _hostDao.findById(vm.getHostId()); - Long clustedId = hostVo.getClusterId(); - Float memoryOvercommitRatio = CapacityManager.MemOverprovisioningFactor.valueIn(clustedId); - Float cpuOvercommitRatio = CapacityManager.CpuOverprovisioningFactor.valueIn(clustedId); - boolean divideMemoryByOverprovisioning = HypervisorGuruBase.VmMinMemoryEqualsMemoryDividedByMemOverprovisioningFactor.valueIn(clustedId); - boolean divideCpuByOverprovisioning = HypervisorGuruBase.VmMinCpuSpeedEqualsCpuSpeedDividedByCpuOverprovisioningFactor.valueIn(clustedId); + Long clusterId = hostVo.getClusterId(); + Float memoryOvercommitRatio = CapacityManager.MemOverprovisioningFactor.valueIn(clusterId); + Float cpuOvercommitRatio = CapacityManager.CpuOverprovisioningFactor.valueIn(clusterId); + boolean divideMemoryByOverprovisioning = HypervisorGuruBase.VmMinMemoryEqualsMemoryDividedByMemOverprovisioningFactor.valueIn(clusterId); + boolean divideCpuByOverprovisioning = HypervisorGuruBase.VmMinCpuSpeedEqualsCpuSpeedDividedByCpuOverprovisioningFactor.valueIn(clusterId); int minMemory = (int)(newServiceOffering.getRamSize() / (divideMemoryByOverprovisioning ? memoryOvercommitRatio : 1)); int minSpeed = (int)(newServiceOffering.getSpeed() / (divideCpuByOverprovisioning ? cpuOvercommitRatio : 1)); - ScaleVmCommand scaleVmCommand = - new ScaleVmCommand(vm.getInstanceName(), newServiceOffering.getCpu(), minSpeed, - newServiceOffering.getSpeed(), minMemory * 1024L * 1024L, newServiceOffering.getRamSize() * 1024L * 1024L, newServiceOffering.getLimitCpuUse()); + Double cpuQuotaPercentage = null; + if (newServiceOffering.getLimitCpuUse() && vm.getHypervisorType().equals(HypervisorType.KVM)) { + KVMGuru kvmGuru = (KVMGuru) _hvGuruMgr.getGuru(vm.getHypervisorType()); + cpuQuotaPercentage = kvmGuru.getCpuQuotaPercentage(minSpeed, hostVo.getSpeed()); + } + + boolean limitCpuUseChange = oldServiceOffering.getLimitCpuUse() != newServiceOffering.getLimitCpuUse(); + ScaleVmCommand scaleVmCommand = new ScaleVmCommand(vm.getInstanceName(), newServiceOffering.getCpu(), minSpeed, newServiceOffering.getSpeed(), + minMemory * 1024L * 1024L, newServiceOffering.getRamSize() * 1024L * 1024L, + newServiceOffering.getLimitCpuUse(), cpuQuotaPercentage, limitCpuUseChange); scaleVmCommand.getVirtualMachine().setId(vm.getId()); scaleVmCommand.getVirtualMachine().setUuid(vm.getUuid()); @@ -5060,16 +5306,20 @@ private VMInstanceVO orchestrateReConfigureVm(String vmUuid, ServiceOffering old throw new CloudRuntimeException("Unable to scale vm due to " + (reconfigureAnswer == null ? "" : reconfigureAnswer.getDetails())); } - upgradeVmDb(vm.getId(), newServiceOffering, oldServiceOffering); + if (reconfiguringOnExistingHost) { + _capacityMgr.releaseVmCapacity(vm, false, false, vm.getHostId()); + } + + boolean vmUpgraded = upgradeVmDb(vm.getId(), newServiceOffering, oldServiceOffering); + if (vmUpgraded) { + vm = _vmDao.findById(vm.getId()); + } if (vm.getType().equals(VirtualMachine.Type.User)) { _userVmMgr.generateUsageEvent(vm, vm.isDisplayVm(), EventTypes.EVENT_VM_DYNAMIC_SCALE); } if (reconfiguringOnExistingHost) { - vm.setServiceOfferingId(oldServiceOffering.getId()); - _capacityMgr.releaseVmCapacity(vm, false, false, vm.getHostId()); - vm.setServiceOfferingId(newServiceOffering.getId()); _capacityMgr.allocateVmCapacity(vm, false); } @@ -5098,9 +5348,20 @@ private void removeCustomOfferingDetails(long vmId) { private void saveCustomOfferingDetails(long vmId, ServiceOffering serviceOffering) { 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()); + + // We need to restore only the customizable parameters. If we save a parameter that is not customizable and attempt + // to restore a VM snapshot, com.cloud.vm.UserVmManagerImpl.validateCustomParameters will fail. + ServiceOffering unfilledOffering = _serviceOfferingDao.findByIdIncludingRemoved(serviceOffering.getId()); + if (unfilledOffering.getCpu() == null) { + details.put(UsageEventVO.DynamicParameters.cpuNumber.name(), serviceOffering.getCpu().toString()); + } + if (unfilledOffering.getSpeed() == null) { + details.put(UsageEventVO.DynamicParameters.cpuSpeed.name(), serviceOffering.getSpeed().toString()); + } + if (unfilledOffering.getRamSize() == null) { + details.put(UsageEventVO.DynamicParameters.memory.name(), serviceOffering.getRamSize().toString()); + } + List detailList = new ArrayList<>(); for (Map.Entry entry: details.entrySet()) { VMInstanceDetailVO detailVO = new VMInstanceDetailVO(vmId, entry.getKey(), entry.getValue(), true); @@ -5121,7 +5382,7 @@ public ConfigKey[] getConfigKeys() { VmConfigDriveLabel, VmConfigDriveOnPrimaryPool, VmConfigDriveForceHostCacheUse, VmConfigDriveUseHostCacheOnUnsupportedPool, HaVmRestartHostUp, ResourceCountRunningVMsonly, AllowExposeHypervisorHostname, AllowExposeHypervisorHostnameAccountLevel, SystemVmRootDiskSize, AllowExposeDomainInMetadata, MetadataCustomCloudName, VmMetadataManufacturer, VmMetadataProductName, - VmSyncPowerStateTransitioning + VmSyncPowerStateTransitioning, SystemVmEnableUserData }; } @@ -5236,22 +5497,22 @@ private void handlePowerOnReportWithNoPendingJobsOnVM(final VMInstanceVO vm) { case Destroyed: case Expunging: - logger.info("Receive power on report when VM is in destroyed or expunging state. vm: {}, state: {}.", vm, vm.getState()); + logger.info("Receive power on report when Instance is in destroyed or expunging state. Instance: {}, state: {}.", vm, vm.getState()); break; case Migrating: - logger.info("VM {} is at {} and we received a power-on report while there is no pending jobs on it.", vm, vm.getState()); + logger.info("Instance {} is at {} and we received a power-on report while there is no pending jobs on it.", vm, vm.getState()); try { stateTransitTo(vm, VirtualMachine.Event.FollowAgentPowerOnReport, vm.getPowerHostId()); } catch (final NoTransitionException e) { - logger.warn("Unexpected VM state transition exception, race-condition?", e); + logger.warn("Unexpected Instance state transition exception, race-condition?", e); } - logger.info("VM {} is sync-ed to at Running state according to power-on report from hypervisor.", vm); + logger.info("Instance {} is sync-ed to at Running state according to power-on report from hypervisor.", vm); break; case Error: default: - logger.info("Receive power on report when VM is in error or unexpected state. vm: {}, state: {}.", vm, vm.getState()); + logger.info("Receive power on report when Instance is in error or unexpected state. Instance: {}, state: {}.", vm, vm.getState()); break; } } @@ -6009,7 +6270,7 @@ public UserVm restoreVirtualMachine(final long vmId, final Long newTemplateId, f } private UserVm orchestrateRestoreVirtualMachine(final long vmId, final Long newTemplateId, final Long rootDiskOfferingId, final boolean expunge, final Map details) throws ResourceUnavailableException, InsufficientCapacityException { - logger.debug("Restoring vm {} with templateId: {}, diskOfferingId: {}, details: {}", vmId, newTemplateId, rootDiskOfferingId, details); + logger.debug("Restoring Instance {} with templateId: {}, diskOfferingId: {}, details: {}", vmId, newTemplateId, rootDiskOfferingId, details); final CallContext context = CallContext.current(); final Account account = context.getCallingAccount(); return _userVmService.restoreVirtualMachine(account, vmId, newTemplateId, rootDiskOfferingId, expunge, details); @@ -6129,6 +6390,80 @@ private Pair orchestrateUpdateDefaultNic(final VmWorkUpd _jobMgr.marshallResultObject(result)); } + @Override + public boolean updateVmNic(VirtualMachine vm, Nic nic, Boolean enabled) { + Outcome outcome = updateVmNicThroughJobQueue(vm, nic, enabled); + + retrieveVmFromJobOutcome(outcome, vm.getUuid(), "updateVmNic"); + + try { + Object jobResult = retrieveResultFromJobOutcomeAndThrowExceptionIfNeeded(outcome); + if (jobResult instanceof Boolean) { + return BooleanUtils.isTrue((Boolean) jobResult); + } + } catch (ResourceUnavailableException | InsufficientCapacityException ex) { + throw new CloudRuntimeException(String.format("Exception while updating VM [%s] NIC. Check the logs for more information.", vm.getUuid())); + } + throw new CloudRuntimeException("Unexpected job execution result."); + } + + private boolean orchestrateUpdateVmNic(final VirtualMachine vm, final Nic nic, final Boolean enabled) throws ResourceUnavailableException { + if (vm.getState() == State.Running) { + try { + UpdateVmNicCommand updateVmNicCmd = new UpdateVmNicCommand(nic.getMacAddress(), vm.getName(), enabled); + Commands cmds = new Commands(Command.OnError.Stop); + cmds.addCommand("updatevmnic", updateVmNicCmd); + + _agentMgr.send(vm.getHostId(), cmds); + + UpdateVmNicAnswer updateVmNicAnswer = cmds.getAnswer(UpdateVmNicAnswer.class); + if (updateVmNicAnswer == null || !updateVmNicAnswer.getResult()) { + logger.warn("Unable to update VM %s NIC [{}].", vm.getName(), nic.getUuid()); + return false; + } + } catch (final OperationTimedoutException e) { + throw new AgentUnavailableException(String.format("Unable to update NIC %s for VM %s.", nic.getUuid(), vm.getUuid()), vm.getHostId(), e); + } + } + + NicVO nicVo = _nicsDao.findById(nic.getId()); + nicVo.setEnabled(enabled); + _nicsDao.persist(nicVo); + + return true; + } + + public Outcome updateVmNicThroughJobQueue(final VirtualMachine vm, final Nic nic, final Boolean isNicEnabled) { + Long vmId = vm.getId(); + String commandName = VmWorkUpdateNic.class.getName(); + Pair pendingWorkJob = retrievePendingWorkJob(vmId, commandName); + + VmWorkJobVO workJob = pendingWorkJob.first(); + + if (workJob == null) { + Pair newVmWorkJobAndInfo = createWorkJobAndWorkInfo(commandName, vmId); + + workJob = newVmWorkJobAndInfo.first(); + VmWorkUpdateNic workInfo = new VmWorkUpdateNic(newVmWorkJobAndInfo.second(), nic.getId(), isNicEnabled); + + setCmdInfoAndSubmitAsyncJob(workJob, workInfo, vmId); + } + AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(workJob.getId()); + + return new VmJobVirtualMachineOutcome(workJob, vmId); + } + + @ReflectionUse + private Pair orchestrateUpdateVmNic(final VmWorkUpdateNic work) throws Exception { + VMInstanceVO vm = findVmById(work.getVmId()); + final NicVO nic = _entityMgr.findById(NicVO.class, work.getNicId()); + if (nic == null) { + throw new CloudRuntimeException(String.format("Unable to find NIC with ID %s.", work.getNicId())); + } + final boolean result = orchestrateUpdateVmNic(vm, nic, work.isEnabled()); + return new Pair<>(JobInfo.Status.SUCCEEDED, _jobMgr.marshallResultObject(result)); + } + private Pair findClusterAndHostIdForVmFromVolumes(long vmId) { Long clusterId = null; Long hostId = null; @@ -6170,14 +6505,46 @@ public Pair findClusterAndHostIdForVm(VirtualMachine vm, boolean ski host = host == null ? _hostDao.findById(hostId) : host; if (host != null) { clusterId = host.getClusterId(); + return new Pair<>(clusterId, hostId); } - return new Pair<>(clusterId, hostId); + return findClusterAndHostIdForVmFromVolumes(vm.getId()); } private Pair findClusterAndHostIdForVm(VirtualMachine vm) { return findClusterAndHostIdForVm(vm, false); } + private boolean hasClvmVolumes(long vmId) { + List volumes = _volsDao.findByInstance(vmId); + return volumes.stream() + .map(v -> _storagePoolDao.findById(v.getPoolId())) + .anyMatch(pool -> pool != null && ClvmPoolManager.isClvmPoolType(pool.getPoolType())); + } + + private void executePreMigrationCommand(VMInstanceVO vm, VirtualMachineTO to, long srcHostId) { + if (!(vm.getHypervisorType() == HypervisorType.KVM && hasClvmVolumes(vm.getId()))) { + return; + } + final String vmInstanceName = vm.getInstanceName(); + final String srcHostUuid = _hostDao.findById(srcHostId).getUuid(); + logger.info("Sending PreMigrationCommand to source host {} for VM {} with CLVM volumes", srcHostUuid, vmInstanceName); + final PreMigrationCommand preMigCmd = new PreMigrationCommand(to, vmInstanceName); + Answer preMigAnswer = null; + try { + preMigAnswer = _agentMgr.send(srcHostId, preMigCmd); + if (preMigAnswer == null || !preMigAnswer.getResult()) { + final String details = preMigAnswer != null ? preMigAnswer.getDetails() : "null answer returned"; + final String msg = "Failed to prepare source host for migration: " + details; + logger.error("Failed to prepare source host {} for migration of VM {}: {}", srcHostUuid, vmInstanceName, details); + throw new CloudRuntimeException(msg); + } + logger.info("Successfully prepared source host {} for migration of VM {}", srcHostUuid, vmInstanceName); + } catch (final AgentUnavailableException | OperationTimedoutException e) { + logger.error("Failed to send PreMigrationCommand to source host {}: {}", srcHostUuid, e.getMessage(), e); + throw new CloudRuntimeException("Failed to prepare source host for migration: " + e.getMessage(), e); + } + } + @Override public Pair findClusterAndHostIdForVm(long vmId) { VMInstanceVO vm = _vmDao.findById(vmId); @@ -6451,4 +6818,18 @@ public void checkDeploymentPlan(VirtualMachine virtualMachine, VirtualMachineTem vmProfile), DataCenter.class, plan.getDataCenterId(), areAffinityGroupsAssociated(vmProfile)); } } + + @Override + public boolean isBlankInstanceDefaultTemplate(VirtualMachineTemplate template) { + return KVM_BLANK_VM_TEMPLATE_NAME.equals(template.getUniqueName()); + } + + @Override + public boolean isBlankInstance(VirtualMachineTemplate template) { + if (isBlankInstanceDefaultTemplate(template)) { + return true; + } + return Boolean.TRUE.equals( + MapUtils.getBoolean(CallContext.current().getContextParameters(), ApiConstants.BLANK_INSTANCE)); + } } 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 7a1a39ec0f07..475ed0f37bd2 100644 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachinePowerStateSyncImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachinePowerStateSyncImpl.java @@ -89,7 +89,7 @@ protected void updateAndPublishVmPowerStates(long hostId, Map hostCache.get(hostId), () -> vmCache.get(vmId), () -> instancePowerStates.get(vmId)); _messageBus.publish(null, VirtualMachineManager.Topics.VM_POWER_STATE, @@ -158,8 +158,8 @@ private void processMissingVmReport(long hostId, Set vmIds, boolean force) // an update might have occurred that we should not override in case of out of band migration instancePowerStates.put(instance.getId(), VirtualMachine.PowerState.PowerReportMissing); } else { - logger.debug("vm id: {} - time since last state update({} ms) has not passed graceful period yet", - instance.getId(), milliSecondsSinceLastStateUpdate); + logger.debug("vm id: {} - time since last state update({} ms) has not passed graceful period ({} ms) yet", + instance.getId(), milliSecondsSinceLastStateUpdate, milliSecondsGracefulPeriod); } } updateAndPublishVmPowerStates(hostId, instancePowerStates, startTime); diff --git a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/AccountTest.java b/engine/orchestration/src/main/java/com/cloud/vm/VmWorkUpdateNic.java similarity index 62% rename from framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/AccountTest.java rename to engine/orchestration/src/main/java/com/cloud/vm/VmWorkUpdateNic.java index 1e62235e71cf..1c63cf34a192 100644 --- a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/AccountTest.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VmWorkUpdateNic.java @@ -14,21 +14,25 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. +package com.cloud.vm; -package org.apache.cloudstack.quota.activationrule.presetvariables; +public class VmWorkUpdateNic extends VmWork { + private static final long serialVersionUID = -8957066627929113278L; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; + Long nicId; + Boolean enabled; -@RunWith(MockitoJUnitRunner.class) -public class AccountTest { + public VmWorkUpdateNic(VmWork vmWork, Long nicId, Boolean enabled) { + super(vmWork); + this.nicId = nicId; + this.enabled = enabled; + } + + public Long getNicId() { + return nicId; + } - @Test - public void setRoleTestAddFieldRoleToCollection() { - Account variable = new Account(); - variable.setRole(null); - Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("role")); + public Boolean isEnabled() { + return enabled; } } diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/dao/EngineHostDaoImpl.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/dao/EngineHostDaoImpl.java index 2ad8d15d0b71..7f6571becc83 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/dao/EngineHostDaoImpl.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/dao/EngineHostDaoImpl.java @@ -213,7 +213,6 @@ public EngineHostDaoImpl() { SequenceSearch = createSearchBuilder(); SequenceSearch.and("id", SequenceSearch.entity().getId(), SearchCriteria.Op.EQ); - // SequenceSearch.addRetrieve("sequence", SequenceSearch.entity().getSequence()); SequenceSearch.done(); DirectlyConnectedSearch = createSearchBuilder(); 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 8639f006383f..9f6d02cc1234 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 @@ -19,6 +19,7 @@ package org.apache.cloudstack.engine.orchestration; import com.cloud.storage.Snapshot; +import com.cloud.storage.VMTemplateVO; import com.cloud.storage.Volume; import com.cloud.template.VirtualMachineTemplate; import java.net.URL; @@ -164,7 +165,7 @@ public void destroyVolume(String volumeEntity) { 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, - List dataDiskInfoList, Volume volume, Snapshot snapshot) throws InsufficientCapacityException { + Long rootDiskKmsKeyId, List dataDiskInfoList, Volume volume, Snapshot snapshot) throws InsufficientCapacityException { // VirtualMachineEntityImpl vmEntity = new VirtualMachineEntityImpl(id, owner, hostName, displayName, cpu, speed, memory, computeTags, rootDiskTags, networks, // vmEntityManager); @@ -198,6 +199,7 @@ public VirtualMachineEntity createVirtualMachine(String id, String owner, String } rootDiskOfferingInfo.setDiskOffering(rootDiskOffering); rootDiskOfferingInfo.setSize(rootDiskSize); + rootDiskOfferingInfo.setKmsKeyId(rootDiskKmsKeyId); if (rootDiskOffering.isCustomizedIops() != null && rootDiskOffering.isCustomizedIops()) { Map userVmDetails = _vmInstanceDetailsDao.listDetailsKeyPairs(vm.getId()); @@ -280,7 +282,7 @@ public VirtualMachineEntity createVirtualMachine(String id, String owner, String @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, List dataDiskInfoList, Volume volume, Snapshot snapshot) + Map> extraDhcpOptionMap, Long diskOfferingId, Long rootDiskKmsKeyId, List dataDiskInfoList, Volume volume, Snapshot snapshot) throws InsufficientCapacityException { // VirtualMachineEntityImpl vmEntity = new VirtualMachineEntityImpl(id, owner, hostName, displayName, cpu, speed, memory, computeTags, rootDiskTags, networks, vmEntityManager); @@ -292,9 +294,11 @@ public VirtualMachineEntity createVirtualMachineFromScratch(String id, String ow ServiceOfferingVO computeOffering = _serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); + VMTemplateVO iso = _templateDao.findByIdIncludingRemoved(Long.valueOf(isoId)); + DiskOfferingInfo rootDiskOfferingInfo = new DiskOfferingInfo(); - if (diskOfferingId == null) { + if (diskOfferingId == null && !_itMgr.isBlankInstance(iso)) { throw new InvalidParameterValueException("Installing from ISO requires a disk offering to be specified for the root disk."); } DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId); @@ -314,6 +318,7 @@ public VirtualMachineEntity createVirtualMachineFromScratch(String id, String ow rootDiskOfferingInfo.setDiskOffering(diskOffering); rootDiskOfferingInfo.setSize(size); + rootDiskOfferingInfo.setKmsKeyId(rootDiskKmsKeyId); if (diskOffering.isCustomizedIops() != null && diskOffering.isCustomizedIops()) { Map userVmDetails = _vmInstanceDetailsDao.listDetailsKeyPairs(vm.getId()); @@ -345,7 +350,7 @@ public VirtualMachineEntity createVirtualMachineFromScratch(String id, String ow HypervisorType hypervisorType = HypervisorType.valueOf(hypervisor); - _itMgr.allocate(vm.getInstanceName(), _templateDao.findByIdIncludingRemoved(new Long(isoId)), computeOffering, rootDiskOfferingInfo, dataDiskOfferings, dataDiskDeviceIds, + _itMgr.allocate(vm.getInstanceName(), iso, 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 9609ba7751d9..1441a9957772 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 @@ -22,13 +22,19 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import javax.inject.Inject; +import org.apache.cloudstack.backup.Backup; +import org.apache.cloudstack.backup.InternalBackupJoinVO; +import org.apache.cloudstack.backup.dao.InternalBackupJoinDao; 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.ObjectInDataStoreStateMachine; @@ -40,6 +46,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.storage.ImageStoreService; +import org.apache.cloudstack.storage.backup.BackupObject; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; @@ -87,12 +94,15 @@ public class DataMigrationUtility { HostDao hostDao; @Inject SnapshotDao snapshotDao; + + @Inject + InternalBackupJoinDao internalBackupJoinDao; /** * This function verifies if the given image store contains data objects that are not in any of the following states: * "Ready" "Allocated", "Destroying", "Destroyed", "Failed". If this is the case, and if the migration policy is complete, * the migration is terminated. */ - public boolean filesReadyToMigrate(Long srcDataStoreId, List templates, List snapshots, List volumes) { + public boolean filesReadyToMigrate(Long srcDataStoreId, List templates, List snapshots, List volumes, List backups) { State[] validStates = {State.Ready, State.Allocated, State.Destroying, State.Destroyed, State.Failed}; boolean isReady = true; for (TemplateDataStoreVO template : templates) { @@ -107,14 +117,48 @@ public boolean filesReadyToMigrate(Long srcDataStoreId, List backups) { + List invalidBackupStates = Arrays.asList(Backup.Status.BackingUp, Backup.Status.Restoring); + List invalidBackupCompressionStatus = Arrays.asList(Backup.CompressionStatus.Compressing, Backup.CompressionStatus.FinalizingCompression); + + List> backupChains; + Set backupIdsAlreadyInChain = new HashSet<>(); + + for (InternalBackupJoinVO backup : backups) { + if (backup.getStatus() == Backup.Status.BackedUp && !backupIdsAlreadyInChain.contains(backup.getId())) { + backupChains = createBackupChain(backup); + backupChains.forEach(list -> backupIdsAlreadyInChain.add(list.stream().map(BackupObject::getId).findFirst().get())); + + for (List backupVolumeChain : backupChains) { + BackupObject backupObject = backupVolumeChain.get(0); + + if (invalidBackupStates.contains(backupObject.getStatus())) { + logger.debug("Migration is not possible because backup {} is in {} state.", backupObject.getUuid(), backupObject.getStatus()); + return false; + } + + if (invalidBackupCompressionStatus.contains(backupObject.getCompressionStatus())) { + logger.debug("Migration is not possible because backup {} is currently being compressed. Current compression status: {}.", backupObject.getUuid(), backupObject.getCompressionStatus()); + return false; + } + } + } + } + + return true; + } + private boolean filesReadyToMigrate(Long srcDataStoreId) { List templates = templateDataStoreDao.listByStoreId(srcDataStoreId); List snapshots = snapshotDataStoreDao.listByStoreId(srcDataStoreId, DataStoreRole.Image); List volumes = volumeDataStoreDao.listByStoreId(srcDataStoreId); - return filesReadyToMigrate(srcDataStoreId, templates, snapshots, volumes); + List backups = internalBackupJoinDao.listByImageStoreId(srcDataStoreId); + + return filesReadyToMigrate(srcDataStoreId, templates, snapshots, volumes, backups); } protected void checkIfCompleteMigrationPossible(ImageStoreService.MigrationPolicy policy, Long srcDataStoreId) { @@ -173,19 +217,58 @@ protected List getSortedValidSourcesList(DataStore srcDataStore, Map return files; } - protected List getSortedValidSourcesList(DataStore srcDataStore, Map, Long>> snapshotChains, - Map, Long>> childTemplates) { + Map, Long>> childTemplates, Map>, Long>> backupChains) { List files = new ArrayList<>(); files.addAll(getAllReadyTemplates(srcDataStore, childTemplates)); files.addAll(getAllReadySnapshotsAndChains(srcDataStore, snapshotChains)); files.addAll(getAllReadyVolumes(srcDataStore)); + files.addAll(getAllReadyBackupsAndChains(srcDataStore, backupChains)); files = sortFilesOnSize(files, snapshotChains); return files; } + protected List getAllReadyBackupsAndChains(DataStore srcDataStore, Map>, Long>> backupChains) { + List backups = internalBackupJoinDao.listByImageStoreId(srcDataStore.getId()); + return getAllReadyBackupsAndChains(backupChains, backups); + } + + private List getAllReadyBackupsAndChains(Map>, Long>> backupsChains, List backups) { + Set backupIdsToMigrate = backups.stream().map(InternalBackupJoinVO::getId).collect(Collectors.toSet()); + List> backupChains; + Set backupIdsAlreadyInChain = new HashSet<>(); + List files = new LinkedList<>(); + + for (InternalBackupJoinVO backup : backups) { + long backupId = backup.getId(); + + if (backup.getStatus() == Backup.Status.BackedUp && !backupIdsAlreadyInChain.contains(backupId)) { + backupChains = createBackupChain(backup); + backupChains.forEach(list -> backupIdsAlreadyInChain.add(list.stream().map(BackupObject::getId).findFirst().get())); + BackupObject parent = backupChains.get(0).get(0); + files.add(parent); + backupsChains.put(parent, new Pair<>(backupChains, backupChains.stream().map(list -> getTotalChainSize(list.stream() + .filter(back -> backupIdsToMigrate.contains(parent.getId())).collect(Collectors.toList())) + ).reduce(Long::sum).get())); + } + } + + return (List) (List) files; + } + + private List> createBackupChain(InternalBackupJoinVO backup) { + List> chain = new LinkedList<>(); + BackupObject backupObject = BackupObject.getBackupObject(backup); + + chain.addAll(backupObject.getParents(backup.getParentId())); + chain.add(internalBackupJoinDao.listById(backup.getId()).stream().map(BackupObject::getBackupObject).collect(Collectors.toList())); + chain.addAll(backupObject.getChildren()); + + return chain; + } + protected List sortFilesOnSize(List files, Map, Long>> snapshotChains) { Collections.sort(files, new Comparator() { @Override @@ -206,12 +289,22 @@ public int compare(DataObject o1, DataObject o2) { protected List getAllReadyTemplates(DataStore srcDataStore, Map, Long>> childTemplates, List templates) { List files = new LinkedList<>(); + Set idsForMigration = new HashSet<>(); + for (TemplateDataStoreVO template : templates) { - VMTemplateVO templateVO = templateDao.findById(template.getTemplateId()); - if (shouldMigrateTemplate(template, templateVO)) { - files.add(templateFactory.getTemplate(template.getTemplateId(), srcDataStore)); + long templateId = template.getTemplateId(); + if (idsForMigration.contains(templateId)) { + logger.warn("Template store reference [{}] is duplicated; not considering it for migration.", template); + continue; } + VMTemplateVO templateVO = templateDao.findById(templateId); + if (!shouldMigrateTemplate(template, templateVO)) { + continue; + } + files.add(templateFactory.getTemplate(template.getTemplateId(), srcDataStore)); + idsForMigration.add(templateId); } + for (TemplateInfo template: files) { List children = templateDao.listByParentTemplatetId(template.getId()); List temps = new ArrayList<>(); @@ -221,6 +314,7 @@ protected List getAllReadyTemplates(DataStore srcDataStore, Map(temps, getTotalChainSize(temps))); } + return (List) (List) files; } @@ -263,16 +357,37 @@ protected boolean shouldMigrateTemplate(TemplateDataStoreVO template, VMTemplate */ protected List getAllReadySnapshotsAndChains(DataStore srcDataStore, Map, Long>> snapshotChains, List snapshots) { List files = new LinkedList<>(); + Set idsForMigration = new HashSet<>(); + for (SnapshotDataStoreVO snapshot : snapshots) { - SnapshotVO snapshotVO = snapshotDao.findById(snapshot.getSnapshotId()); - if (snapshot.getState() == ObjectInDataStoreStateMachine.State.Ready && - snapshotVO != null && snapshotVO.getHypervisorType() != Hypervisor.HypervisorType.Simulator - && snapshot.getParentSnapshotId() == 0 ) { - SnapshotInfo snap = snapshotFactory.getSnapshot(snapshotVO.getSnapshotId(), snapshot.getDataStoreId(), snapshot.getRole()); - if (snap != null) { - files.add(snap); - } + long snapshotId = snapshot.getSnapshotId(); + if (idsForMigration.contains(snapshotId)) { + logger.warn("Snapshot store reference [{}] is duplicated; not considering it for migration.", snapshot); + continue; } + if (snapshot.getState() != ObjectInDataStoreStateMachine.State.Ready) { + logger.warn("Not migrating snapshot [{}] because its state is not ready.", snapshot); + continue; + } + SnapshotVO snapshotVO = snapshotDao.findById(snapshotId); + if (snapshotVO == null) { + logger.debug("Not migrating snapshot [{}] because we could not find its database entry.", snapshot); + continue; + } + if (snapshotVO.getHypervisorType() == Hypervisor.HypervisorType.Simulator) { + logger.debug("Not migrating snapshot [{}] because its hypervisor type is simulator.", snapshot); + continue; + } + if (snapshot.getParentSnapshotId() != 0) { + continue; // The child snapshot will be migrated in the for loop below. + } + SnapshotInfo snap = snapshotFactory.getSnapshot(snapshotVO.getSnapshotId(), snapshot.getDataStoreId(), snapshot.getRole()); + if (snap == null) { + logger.debug("Not migrating snapshot [{}] because we could not get its information.", snapshot); + continue; + } + files.add(snap); + idsForMigration.add(snapshotId); } for (SnapshotInfo parent : files) { @@ -285,7 +400,7 @@ protected List getAllReadySnapshotsAndChains(DataStore srcDataStore, chain.addAll(children); } } - snapshotChains.put(parent, new Pair, Long>(chain, getTotalChainSize(chain))); + snapshotChains.put(parent, new Pair<>(chain, getTotalChainSize(chain))); } return (List) (List) files; @@ -306,14 +421,31 @@ protected Long getTotalChainSize(List chain) { protected List getAllReadyVolumes(DataStore srcDataStore, List volumes) { List files = new LinkedList<>(); + Set idsForMigration = new HashSet<>(); + for (VolumeDataStoreVO volume : volumes) { - if (volume.getState() == ObjectInDataStoreStateMachine.State.Ready) { - VolumeInfo volumeInfo = volumeFactory.getVolume(volume.getVolumeId(), srcDataStore); - if (volumeInfo != null && volumeInfo.getHypervisorType() != Hypervisor.HypervisorType.Simulator) { - files.add(volumeInfo); - } + long volumeId = volume.getVolumeId(); + if (idsForMigration.contains(volumeId)) { + logger.warn("Volume store reference [{}] is duplicated; not considering it for migration.", volume); + continue; + } + if (volume.getState() != ObjectInDataStoreStateMachine.State.Ready) { + logger.debug("Not migrating volume [{}] because its state is not ready.", volume); + continue; + } + VolumeInfo volumeInfo = volumeFactory.getVolume(volume.getVolumeId(), srcDataStore); + if (volumeInfo == null) { + logger.debug("Not migrating volume [{}] because we could not get its information.", volume); + continue; } + if (volumeInfo.getHypervisorType() == Hypervisor.HypervisorType.Simulator) { + logger.debug("Not migrating volume [{}] because its hypervisor type is simulator.", volume); + continue; + } + files.add(volumeInfo); + idsForMigration.add(volumeId); } + return files; } @@ -325,10 +457,9 @@ protected List getAllReadyVolumes(DataStore srcDataStore) { /** Returns the count of active SSVMs - SSVM with agents in connected state, so as to dynamically increase the thread pool * size when SSVMs scale */ - protected int activeSSVMCount(DataStore dataStore) { - long datacenterId = dataStore.getScope().getScopeId(); + protected int activeSSVMCount(Long zoneId) { List ssvms = - secStorageVmDao.getSecStorageVmListInStates(null, datacenterId, VirtualMachine.State.Running, VirtualMachine.State.Migrating); + secStorageVmDao.getSecStorageVmListInStates(null, zoneId, VirtualMachine.State.Running, VirtualMachine.State.Migrating); int activeSSVMs = 0; for (SecondaryStorageVmVO vm : ssvms) { String name = "s-"+vm.getId()+"-VM"; 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 146643bae22c..84a397349cec 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 @@ -58,6 +58,7 @@ import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.network.RoutedIpv4Manager; import org.apache.cloudstack.network.dao.NetworkPermissionDao; +import org.apache.cloudstack.reservation.dao.ReservationDao; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.ObjectUtils; @@ -86,6 +87,7 @@ import com.cloud.api.query.vo.DomainRouterJoinVO; import com.cloud.bgp.BGPService; import com.cloud.configuration.ConfigurationManager; +import com.cloud.configuration.Resource; import com.cloud.configuration.Resource.ResourceType; import com.cloud.dc.ASNumberVO; import com.cloud.dc.ClusterVO; @@ -175,6 +177,10 @@ import com.cloud.network.dao.RemoteAccessVpnDao; import com.cloud.network.dao.RemoteAccessVpnVO; import com.cloud.network.dao.RouterNetworkDao; +import org.apache.cloudstack.extension.Extension; +import org.apache.cloudstack.extension.ExtensionHelper; +import org.apache.cloudstack.framework.extensions.network.NetworkExtensionElement; + import com.cloud.network.element.AggregatedCommandExecutor; import com.cloud.network.element.ConfigDriveNetworkElement; import com.cloud.network.element.DhcpServiceProvider; @@ -214,6 +220,7 @@ import com.cloud.offerings.dao.NetworkOfferingDetailsDao; import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; import com.cloud.resource.ResourceManager; +import com.cloud.resourcelimit.CheckedReservation; import com.cloud.server.ManagementServer; import com.cloud.user.Account; import com.cloud.user.ResourceLimitService; @@ -310,6 +317,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra @Inject NetworkOfferingDetailsDao _ntwkOffDetailsDao; @Inject + NetworkOfferingServiceMapDao networkOfferingServiceMapDao; + @Inject AccountGuestVlanMapDao _accountGuestVlanMapDao; @Inject DataCenterVnetDao _datacenterVnetDao; @@ -365,6 +374,10 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra private BGPService bgpService; @Inject private Ipv6GuestPrefixSubnetNetworkMapDao ipv6GuestPrefixSubnetNetworkMapDao; + @Inject + protected ExtensionHelper extensionHelper; + @Inject + private NetworkExtensionElement networkExtensionElement; @Override public List getNetworkGurus() { @@ -447,6 +460,8 @@ public void setDhcpProviders(final List dhcpProviders) { ClusterDao clusterDao; @Inject RoutedIpv4Manager routedIpv4Manager; + @Inject + private ReservationDao reservationDao; protected StateMachine2 _stateMachine; ScheduledExecutorService _executor; @@ -454,7 +469,29 @@ public void setDhcpProviders(final List dhcpProviders) { SearchBuilder AssignIpAddressSearch; SearchBuilder AssignIpAddressFromPodVlanSearch; - HashMap _lastNetworkIdsToFree = new HashMap(); + HashMap _lastNetworkIdsToFree = new HashMap<>(); + + /** + * Returns the full list of network elements to iterate when implementing, + * shutting down, or otherwise orchestrating a network. + * + *

The base list ({@link #networkElements}, wired by Spring) is extended + * at runtime with one transient {@link NetworkExtensionElement} per + * registered {@code NetworkOrchestrator} extension. This keeps the + * Spring bean list free from {@code NetworkExtensionElement} and allows + * dynamic discovery of extensions without a restart.

+ */ + protected List getNetworkElementsIncludingExtensions() { + List extensions = extensionHelper.listExtensionsByType(Extension.Type.NetworkOrchestrator); + if (CollectionUtils.isEmpty(extensions)) { + return networkElements; + } + List combined = new ArrayList<>(networkElements); + for (Extension ext : extensions) { + combined.add(networkExtensionElement.withProviderName(ext.getName())); + } + return combined; + } private void updateRouterDefaultDns(final VirtualMachineProfile vmProfile, final NicProfile nicProfile) { if (!Type.DomainRouter.equals(vmProfile.getType()) || !nicProfile.isDefaultNic()) { @@ -492,8 +529,8 @@ private void updateRouterDefaultDns(final VirtualMachineProfile vmProfile, final @DB public boolean configure(final String name, final Map params) throws ConfigurationException { // populate providers - final Map> defaultSharedNetworkOfferingProviders = new HashMap>(); - final Set defaultProviders = new HashSet(); + final Map> defaultSharedNetworkOfferingProviders = new HashMap<>(); + final Set defaultProviders = new HashSet<>(); final Set tungstenProvider = new HashSet<>(); defaultProviders.add(Network.Provider.VirtualRouter); @@ -512,11 +549,11 @@ public boolean configure(final String name, final Map params) th defaultIsolatedNetworkOfferingProviders.put(Service.PortForwarding, defaultProviders); defaultIsolatedNetworkOfferingProviders.put(Service.Vpn, defaultProviders); - final Map> defaultSharedSGEnabledNetworkOfferingProviders = new HashMap>(); + final Map> defaultSharedSGEnabledNetworkOfferingProviders = new HashMap<>(); defaultSharedSGEnabledNetworkOfferingProviders.put(Service.Dhcp, defaultProviders); defaultSharedSGEnabledNetworkOfferingProviders.put(Service.Dns, defaultProviders); defaultSharedSGEnabledNetworkOfferingProviders.put(Service.UserData, defaultProviders); - final Set sgProviders = new HashSet(); + final Set sgProviders = new HashSet<>(); sgProviders.add(Provider.SecurityGroupProvider); defaultSharedSGEnabledNetworkOfferingProviders.put(Service.SecurityGroup, sgProviders); @@ -529,7 +566,7 @@ public boolean configure(final String name, final Map params) th defaultTungstenSharedSGEnabledNetworkOfferingProviders.put(Service.SecurityGroup, tungstenProvider); - final Map> defaultIsolatedSourceNatEnabledNetworkOfferingProviders = new HashMap>(); + final Map> defaultIsolatedSourceNatEnabledNetworkOfferingProviders = new HashMap<>(); defaultProviders.clear(); defaultProviders.add(Network.Provider.VirtualRouter); defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.Dhcp, defaultProviders); @@ -543,7 +580,7 @@ public boolean configure(final String name, final Map params) th defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.PortForwarding, defaultProviders); defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.Vpn, defaultProviders); - final Map> defaultVPCOffProviders = new HashMap>(); + final Map> defaultVPCOffProviders = new HashMap<>(); defaultProviders.clear(); defaultProviders.add(Network.Provider.VPCVirtualRouter); defaultVPCOffProviders.put(Service.Dhcp, defaultProviders); @@ -556,15 +593,16 @@ public boolean configure(final String name, final Map params) th defaultVPCOffProviders.put(Service.StaticNat, defaultProviders); defaultVPCOffProviders.put(Service.PortForwarding, defaultProviders); defaultVPCOffProviders.put(Service.Vpn, defaultProviders); + defaultVPCOffProviders.put(Service.Firewall, defaultProviders); Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(final TransactionStatus status) { - NetworkOfferingVO offering = null; + NetworkOfferingVO offering; //#1 - quick cloud network offering 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, + Availability.Optional, null, new HashMap<>(), true, Network.GuestType.Shared, false, null, true, null, true, false, null, false, null, true, false, false, false, false, null, null, null, true, null, null, false); } @@ -622,11 +660,11 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { } //#8 - network offering with internal lb service - final Map> internalLbOffProviders = new HashMap>(); - final Set defaultVpcProvider = new HashSet(); + final Map> internalLbOffProviders = new HashMap<>(); + final Set defaultVpcProvider = new HashSet<>(); defaultVpcProvider.add(Network.Provider.VPCVirtualRouter); - final Set defaultInternalLbProvider = new HashSet(); + final Set defaultInternalLbProvider = new HashSet<>(); defaultInternalLbProvider.add(Network.Provider.InternalLbVm); internalLbOffProviders.put(Service.Dhcp, defaultVpcProvider); @@ -646,12 +684,12 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { _networkOfferingDao.update(offering.getId(), offering); } - final Map> netscalerServiceProviders = new HashMap>(); - final Set vrProvider = new HashSet(); + final Map> netscalerServiceProviders = new HashMap<>(); + final Set vrProvider = new HashSet<>(); vrProvider.add(Provider.VirtualRouter); - final Set sgProvider = new HashSet(); + final Set sgProvider = new HashSet<>(); sgProvider.add(Provider.SecurityGroupProvider); - final Set nsProvider = new HashSet(); + final Set nsProvider = new HashSet<>(); nsProvider.add(Provider.Netscaler); netscalerServiceProviders.put(Service.Dhcp, vrProvider); netscalerServiceProviders.put(Service.Dns, vrProvider); @@ -660,10 +698,10 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { netscalerServiceProviders.put(Service.StaticNat, nsProvider); netscalerServiceProviders.put(Service.Lb, nsProvider); - final Map> serviceCapabilityMap = new HashMap>(); - final Map elb = new HashMap(); + final Map> serviceCapabilityMap = new HashMap<>(); + final Map elb = new HashMap<>(); elb.put(Capability.ElasticLb, "true"); - final Map eip = new HashMap(); + final Map eip = new HashMap<>(); eip.put(Capability.ElasticIp, "true"); serviceCapabilityMap.put(Service.Lb, elb); serviceCapabilityMap.put(Service.StaticNat, eip); @@ -726,11 +764,6 @@ public boolean start() { return true; } - @Override - public boolean stop() { - return true; - } - protected NetworkOrchestrator() { setStateMachine(); } @@ -772,11 +805,11 @@ public List setupNetwork(final Account owner, final NetworkOf } } - final List networks = new ArrayList(); + final List networks = new ArrayList<>(); long related = -1; - for (final NetworkGuru guru : networkGurus) { + for (final NetworkGuru guru : getDesignNetworkGurus(offering)) { final Network network = guru.design(offering, plan, predefined, name, vpcId, owner); if (network == null) { continue; @@ -852,6 +885,46 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { } } + private List getDesignNetworkGurus(final NetworkOffering offering) { + if (!isIsolationMethodNetworkExtension(offering.getId())) { + return networkGurus; + } + + List extensionGurus = networkGurus.stream() + .filter(guru -> ExtensionHelper.NETWORK_EXTENSION_GURU_NAME.equals(guru.getName())) + .collect(Collectors.toList()); + if (CollectionUtils.isNotEmpty(extensionGurus)) { + return extensionGurus; + } + + logger.warn("Network offering {} requests {}={}, but {} is not registered; using default guru ordering", + offering.getUuid(), ExtensionHelper.NETWORK_ISOLATION_METHOD_DETAIL_KEY, + ExtensionHelper.NETWORK_EXTENSION_ISOLATION_METHOD, ExtensionHelper.NETWORK_EXTENSION_GURU_NAME); + return networkGurus; + } + + @Override + public boolean isIsolationMethodNetworkExtension(final Long networkOfferingId) { + if (networkOfferingId == null) { + return false; + } + + List providers = networkOfferingServiceMapDao.getDistinctProviders(networkOfferingId); + if (CollectionUtils.isEmpty(providers)) { + return false; + } + + for (String providerName : providers) { + if (!extensionHelper.isNetworkExtensionProvider(providerName)) { + continue; + } + if (extensionHelper.usesNetworkExtensionIsolation(providerName)) { + return true; + } + } + return false; + } + @NotNull private static NetworkVO getNetworkVO(long id, final NetworkOffering offering, final DeploymentPlan plan, final Network predefined, Network network, final NetworkGuru guru, final Account owner, @@ -863,6 +936,7 @@ private static NetworkVO getNetworkVO(long id, final NetworkOffering offering, f vpcId, offering.isRedundantRouter(), predefined.getExternalId()); vo.setDisplayNetwork(isDisplayNetworkEnabled == null || isDisplayNetworkEnabled); vo.setStrechedL2Network(offering.isSupportingStrechedL2()); + vo.setKeepMacAddressOnPublicNic(predefined.getKeepMacAddressOnPublicNic()); return vo; } @@ -895,7 +969,7 @@ public void allocate(final VirtualMachineProfile vm, final LinkedHashMap> profilesList = getOrderedNetworkNicProfileMapping(networks); - final List nics = new ArrayList(size); + final List nics = new ArrayList<>(size); NicProfile defaultNic = null; Network nextNetwork = null; for (Pair networkNicPair : profilesList) { @@ -939,17 +1013,17 @@ private Pair addRequestedNicToNicListWithDeviceNumberAndRet final int devId = vmNic.getDeviceId(); if (devId >= deviceIds.length) { - throw new IllegalArgumentException("Device id for nic is too large: " + vmNic); + throw new IllegalArgumentException("Device ID for NIC is too large: " + vmNic); } if (deviceIds[devId]) { - throw new IllegalArgumentException("Conflicting device id for two different nics: " + vmNic); + throw new IllegalArgumentException("Conflicting device ID for two different NICs: " + vmNic); } deviceIds[devId] = true; if (vmNic.isDefaultNic()) { if (defaultNic != null) { - throw new IllegalArgumentException("You cannot specify two nics as default nics: nic 1 = " + defaultNic + "; nic 2 = " + vmNic); + throw new IllegalArgumentException("You cannot specify two NICs as default NICs: NIC 1 = " + defaultNic + "; NIC 2 = " + vmNic); } defaultNic = vmNic; } @@ -972,16 +1046,16 @@ private List> getOrderedNetworkNicProfileMapping(final for (final Map.Entry> network : networks.entrySet()) { List requestedProfiles = network.getValue(); if (requestedProfiles == null) { - requestedProfiles = new ArrayList(); + requestedProfiles = new ArrayList<>(); } if (requestedProfiles.isEmpty()) { requestedProfiles.add(null); } for (final NicProfile requested : requestedProfiles) { - profilesList.add(new Pair(network.getKey(), requested)); + profilesList.add(new Pair<>(network.getKey(), requested)); } } - profilesList.sort(new Comparator>() { + profilesList.sort(new Comparator<>() { @Override public int compare(Pair pair1, Pair pair2) { int profile1Order = Integer.MAX_VALUE; @@ -1030,9 +1104,9 @@ private int determineNumberOfNicsRequired(final VirtualMachineProfile vm, final */ private void createExtraNics(final VirtualMachineProfile vm, int size, List nics, Network finalNetwork) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException { if (nics.size() != size) { - logger.warn("Number of nics {} doesn't match number of requested nics {}", nics.size(), size); + logger.warn("Number of NICs {} doesn't match number of requested NICs {}", nics.size(), size); if (nics.size() > size) { - throw new CloudRuntimeException("Number of nics " + nics.size() + " doesn't match number of requested networks " + size); + throw new CloudRuntimeException("Number of NICs " + nics.size() + " doesn't match number of requested networks " + size); } else { if (finalNetwork == null) { throw new CloudRuntimeException(String.format("can not assign network to %d remaining required NICs", size - nics.size())); @@ -1064,7 +1138,7 @@ public void saveExtraDhcpOptions(final String networkUuid, final Long nicId, fin } private NicVO persistNicAfterRaceCheck(final NicVO nic, final Long networkId, final NicProfile profile, int deviceId) { - return Transaction.execute(new TransactionCallback() { + return Transaction.execute(new TransactionCallback<>() { @Override public NicVO doInTransaction(TransactionStatus status) { NicVO vo = _nicDao.findNonPlaceHolderByIp4AddressAndNetworkId(profile.getIPv4Address(), networkId); @@ -1082,10 +1156,10 @@ public NicVO doInTransaction(TransactionStatus status) { private NicVO checkForRaceAndAllocateNic(final NicProfile requested, final Network network, final Boolean isDefaultNic, int deviceId, final VirtualMachineProfile vm) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException { final NetworkVO ntwkVO = _networksDao.findById(network.getId()); - logger.debug("Allocating nic for vm {} in network {} with requested profile {}", vm.getVirtualMachine(), network, requested); + logger.debug("Allocating NIC for Instance {} in Network {} with requested profile {}", vm.getVirtualMachine(), network, requested); final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, ntwkVO.getGuruName()); - NicVO vo = null; + NicVO vo; boolean retryIpAllocation; do { retryIpAllocation = false; @@ -1161,7 +1235,7 @@ public Pair allocateNic(final NicProfile requested, final N _nicDao.update(vo.getId(), vo); setMtuInVRNicProfile(networks, network.getTrafficType(), vmNic); } - return new Pair(vmNic, Integer.valueOf(deviceId)); + return new Pair<>(vmNic, Integer.valueOf(deviceId)); } private boolean isNicAllocatedForProviderPublicNetworkOnVR(Network network, NicProfile requested, VirtualMachineProfile vm, Provider provider) { @@ -1288,7 +1362,7 @@ protected void configureNicProfileBasedOnRequestedIp(NicProfile requestedNicProf nicProfile.setIPv4Gateway(ipv4Gateway); nicProfile.setIPv4Netmask(ipv4Netmask); - if (nicProfile.getMacAddress() == null) { + if (nicProfile.getMacAddress() == null || !_networkModel.isMACUnique(nicProfile.getMacAddress(), network.getId())) { try { String macAddress = _networkModel.getNextAvailableMacAddressInNetwork(network.getId()); nicProfile.setMacAddress(macAddress); @@ -1443,7 +1517,7 @@ boolean isNetworkImplemented(final NetworkVO network) { Pair implementNetwork(final long networkId, final DeployDestination dest, final ReservationContext context, final boolean isRouter) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { - Pair implemented = null; + Pair implemented; if (!isRouter) { implemented = implementNetwork(networkId, dest, context); } else { @@ -1454,7 +1528,7 @@ Pair implementNetwork(final long networkId, final Deploy // in issues. In order to avoid it, implementNetwork() call for VR is replaced with below code. final NetworkVO network = _networksDao.findById(networkId); final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName()); - implemented = new Pair(guru, network); + implemented = new Pair<>(guru, network); } return implemented; } @@ -1542,7 +1616,7 @@ private boolean networkMeetsPersistenceCriteria(NetworkVO network, NetworkOfferi @DB public Pair implementNetwork(final long networkId, final DeployDestination dest, final ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { - final Pair implemented = new Pair(null, null); + final Pair implemented = new Pair<>(null, null); NetworkVO network = _networksDao.findById(networkId); final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName()); @@ -1592,6 +1666,9 @@ public Pair implementNetwork(final long networkId, final // implement network elements and re-apply all the network rules implementNetworkElementsAndResources(dest, context, network, offering); + // reload network after implementing the network + network = _networksDao.findById(networkId); + long dcId = dest.getDataCenter().getId(); if (networkMeetsPersistenceCriteria(network, offering, false)) { setupPersistentNetwork(network, offering, dcId); @@ -1609,10 +1686,10 @@ public Pair implementNetwork(final long networkId, final return implemented; } catch (final NoTransitionException e) { logger.error(e.getMessage()); - return new Pair(null, null); + return new Pair<>(null, null); } catch (final CloudRuntimeException | OperationTimedoutException e) { logger.error("Caught exception: {}", e.getMessage()); - return new Pair(null, null); + return new Pair<>(null, null); } finally { if (implemented.first() == null) { logger.debug("Cleaning up because we're unable to implement the network {}", network); @@ -1656,19 +1733,19 @@ public void implementNetworkElementsAndResources(final DeployDestination dest, f && (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.SourceNat) || _networkModel.areServicesSupportedInNetwork(network.getId(), Service.Gateway)) && (network.getGuestType() == Network.GuestType.Isolated || network.getGuestType() == Network.GuestType.Shared && zone.getNetworkType() == NetworkType.Advanced)) { - List ips = null; + List ips; final Account owner = _entityMgr.findById(Account.class, network.getAccountId()); if (network.getVpcId() != null) { ips = _ipAddressDao.listByAssociatedVpc(network.getVpcId(), true); if (ips.isEmpty()) { final Vpc vpc = _vpcMgr.getActiveVpc(network.getVpcId()); - logger.debug("Creating a source nat ip for vpc {}", vpc); + logger.debug("Creating a source NAT IP for VPC {}", vpc); _vpcMgr.assignSourceNatIpAddressToVpc(owner, vpc, null); } } else { ips = _ipAddressDao.listByAssociatedNetwork(network.getId(), true); if (ips.isEmpty()) { - logger.debug("Creating a source nat ip for network {}", network); + logger.debug("Creating a source NAT IP for Network {}", network); _ipAddrMgr.assignSourceNatIpAddressToGuestNetwork(owner, network); } } @@ -1685,7 +1762,8 @@ public void implementNetworkElementsAndResources(final DeployDestination dest, f } } - for (final NetworkElement element : networkElements) { + List allNetworkElements = getNetworkElementsIncludingExtensions(); + for (final NetworkElement element : allNetworkElements) { if (element instanceof AggregatedCommandExecutor && providersToImplement.contains(element.getProvider())) { ((AggregatedCommandExecutor) element).prepareAggregatedExecution(network, dest); } @@ -1702,7 +1780,7 @@ public void implementNetworkElementsAndResources(final DeployDestination dest, f ex.addProxyObject(_entityMgr.findById(DataCenter.class, network.getDataCenterId()).getUuid()); throw ex; } - for (final NetworkElement element : networkElements) { + for (final NetworkElement element : allNetworkElements) { if (element instanceof AggregatedCommandExecutor && providersToImplement.contains(element.getProvider())) { if (!((AggregatedCommandExecutor) element).completeAggregatedExecution(network, dest)) { logger.warn("Failed to re-program the network as a part of network {} implement due to aggregated commands execution failure!", network); @@ -1716,7 +1794,7 @@ public void implementNetworkElementsAndResources(final DeployDestination dest, f } reconfigureAndApplyStaticRouteForVpcVpn(network); } finally { - for (final NetworkElement element : networkElements) { + for (final NetworkElement element : allNetworkElements) { if (element instanceof AggregatedCommandExecutor && providersToImplement.contains(element.getProvider())) { ((AggregatedCommandExecutor) element).cleanupAggregatedExecution(network, dest); } @@ -1737,7 +1815,7 @@ private void reconfigureAndApplyStaticRouteForVpcVpn(Network network) { 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) { + for (NetworkElement element : getNetworkElementsIncludingExtensions()) { if (providersToImplement.contains(element.getProvider())) { if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) { // The physicalNetworkId will not get translated into a uuid by the response serializer, @@ -1773,13 +1851,13 @@ protected boolean reprogramNetworkRules(final long networkId, final Account call _firewallMgr.applyDefaultEgressFirewallRule(network.getId(), offering.isEgressDefaultPolicy(), true); } if (!_firewallMgr.applyFirewallRules(firewallEgressRulesToApply, false, caller)) { - logger.warn("Failed to reapply firewall Egress rule(s) as a part of network {} restart", network); + logger.warn("Failed to reapply firewall Egress rule(s) as a part of Network {} restart", network); success = false; } // associate all ip addresses if (!_ipAddrMgr.applyIpAssociations(network, false)) { - logger.warn("Failed to apply ip addresses as a part of network {} restart", network); + logger.warn("Failed to apply IP addresses as a part of Network {} restart", network); success = false; } @@ -2030,7 +2108,7 @@ public void doInTransactionWithoutResult(TransactionStatus status) { @Override public void configureUpdateInSequence(Network network) { List providers = getNetworkProviders(network.getId()); - for (NetworkElement element : networkElements) { + for (NetworkElement element : getNetworkElementsIncludingExtensions()) { if (providers.contains(element.getProvider())) { if (element instanceof RedundantResource) { ((RedundantResource) element).configureResource(network); @@ -2043,7 +2121,7 @@ public void configureUpdateInSequence(Network network) { public int getResourceCount(Network network) { List providers = getNetworkProviders(network.getId()); int resourceCount = 0; - for (NetworkElement element : networkElements) { + for (NetworkElement element : getNetworkElementsIncludingExtensions()) { if (providers.contains(element.getProvider())) { //currently only one element implements the redundant resource interface if (element instanceof RedundantResource) { @@ -2074,7 +2152,7 @@ public void configureExtraDhcpOptions(Network network, long nicId) { @Override public void finalizeUpdateInSequence(Network network, boolean success) { List providers = getNetworkProviders(network.getId()); - for (NetworkElement element : networkElements) { + for (NetworkElement element : getNetworkElementsIncludingExtensions()) { if (providers.contains(element.getProvider())) { //currently only one element implements the redundant resource interface if (element instanceof RedundantResource) { @@ -2101,7 +2179,7 @@ public void setHypervisorHostname(VirtualMachineProfile vm, DeployDestination de } private void setHypervisorHostnameInNetwork(VirtualMachineProfile vm, DeployDestination dest, Network network, NicProfile profile, boolean migrationSuccessful) { - for (final NetworkElement element : networkElements) { + for (final NetworkElement element : getNetworkElementsIncludingExtensions()) { if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.UserData) && element instanceof UserDataServiceProvider && (element instanceof ConfigDriveNetworkElement && !migrationSuccessful || element instanceof VirtualRouterElement && migrationSuccessful)) { String errorMsg = String.format("Failed to add hypervisor host name while applying the userdata during the migration of VM %s, " + @@ -2126,7 +2204,7 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { _nicDao.update(nic.getId(), nic); if (nic.getVmType() == VirtualMachine.Type.User) { - logger.debug(String.format("Changing active number of nics for network id=%s on %d", network, count)); + logger.debug(String.format("Changing active number of NICs for Network ID=%s on %d", network, count)); _networksDao.changeActiveNicsBy(network.getId(), count); } @@ -2145,7 +2223,7 @@ public void prepare(final VirtualMachineProfile vmProfile, final DeployDestinati // we have to implement default nics first - to ensure that default network elements start up first in multiple //nics case // (need for setting DNS on Dhcp to domR's Ip4 address) - Collections.sort(nics, new Comparator() { + Collections.sort(nics, new Comparator<>() { @Override public int compare(final NicVO nic1, final NicVO nic2) { @@ -2160,8 +2238,8 @@ public int compare(final NicVO nic1, final NicVO nic2) { final Pair implemented = implementNetwork(nic.getNetworkId(), dest, context, vmProfile.getVirtualMachine().getType() == Type.DomainRouter); if (implemented == null || implemented.first() == null) { NetworkVO network = _networksDao.findById(nic.getNetworkId()); - logger.warn("Failed to implement network: {} as a part of preparing nic {}", network, nic); - throw new CloudRuntimeException(String.format("Failed to implement network id=%s as a part preparing nic %s", network, nic)); + logger.warn("Failed to implement Network: {} as a part of preparing NIC {}", network, nic); + throw new CloudRuntimeException(String.format("Failed to implement Network ID=%s as a part preparing NIC %s", network, nic)); } final NetworkVO network = implemented.second(); @@ -2183,7 +2261,7 @@ public NicProfile prepareNic(final VirtualMachineProfile vmProfile, final Deploy final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName()); final NicVO nic = _nicDao.findById(nicId); - NicProfile profile = null; + NicProfile profile; if (nic.getReservationStrategy() == Nic.ReservationStrategy.Start) { nic.setState(Nic.State.Reserving); nic.setReservationId(context.getReservationId()); @@ -2229,7 +2307,7 @@ public NicProfile prepareNic(final VirtualMachineProfile vmProfile, final Deploy updateNic(nic, network, 1); final List providersToImplement = getNetworkProviders(network.getId()); - for (final NetworkElement element : networkElements) { + for (final NetworkElement element : getNetworkElementsIncludingExtensions()) { if (providersToImplement.contains(element.getProvider())) { if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) { throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: " @@ -2284,7 +2362,7 @@ public void prepareNicForMigration(final VirtualMachineProfile vm, final DeployD } final List providersToImplement = getNetworkProviders(network.getId()); - for (final NetworkElement element : networkElements) { + for (final NetworkElement element : getNetworkElementsIncludingExtensions()) { if (providersToImplement.contains(element.getProvider())) { if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) { throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: " @@ -2328,10 +2406,11 @@ public void prepareAllNicsForMigration(final VirtualMachineProfile vm, final Dep } } final List providersToImplement = getNetworkProviders(network.getId()); - for (final NetworkElement element : networkElements) { + for (final NetworkElement element : getNetworkElementsIncludingExtensions()) { if (providersToImplement.contains(element.getProvider())) { if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) { - throw new CloudRuntimeException(String.format("Service provider %s either doesn't exist or is not enabled in physical network: %s", element.getProvider().getName(), _physicalNetworkDao.findById(network.getPhysicalNetworkId()))); + throw new CloudRuntimeException(String.format("Service provider %s either doesn't exist or is not enabled in physical network: %s", + element.getProvider().getName(), _physicalNetworkDao.findById(network.getPhysicalNetworkId()))); } if (element instanceof NetworkMigrationResponder) { if (!((NetworkMigrationResponder) element).prepareMigration(profile, network, vm, dest, context)) { @@ -2344,7 +2423,7 @@ public void prepareAllNicsForMigration(final VirtualMachineProfile vm, final Dep vm.addNic(profile); } - final List addedURIs = new ArrayList(); + final List addedURIs = new ArrayList<>(); if (guestNetworkId != null) { final List publicIps = _ipAddressDao.listByAssociatedNetwork(guestNetworkId, null); for (final IPAddressVO userIp : publicIps) { @@ -2359,7 +2438,7 @@ public void prepareAllNicsForMigration(final VirtualMachineProfile vm, final Dep final NetworkVO network = _networksDao.findById(ntwkId); final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName()); final NicProfile profile = new NicProfile(); - logger.debug("Creating nic profile for migration. BroadcastUri: {} NetworkId: {} VM: {}", broadcastUri.toString(), network, vm); + logger.debug("Creating NIC profile for migration. BroadcastUri: {} NetworkId: {} Instance: {}", broadcastUri.toString(), network, vm); profile.setDeviceId(255); //dummyId profile.setIPv4Address(userIp.getAddress().toString()); profile.setIPv4Netmask(publicIp.getNetmask()); @@ -2409,7 +2488,7 @@ public void commitNicForMigration(final VirtualMachineProfile src, final Virtual } final List providersToImplement = getNetworkProviders(network.getId()); - for (final NetworkElement element : networkElements) { + for (final NetworkElement element : getNetworkElementsIncludingExtensions()) { if (providersToImplement.contains(element.getProvider())) { if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) { throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: " @@ -2445,7 +2524,7 @@ public void rollbackNicForMigration(final VirtualMachineProfile src, final Virtu } final List providersToImplement = getNetworkProviders(network.getId()); - for (final NetworkElement element : networkElements) { + for (final NetworkElement element : getNetworkElementsIncludingExtensions()) { if (providersToImplement.contains(element.getProvider())) { if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) { throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: " @@ -2481,7 +2560,7 @@ protected void releaseNic(final VirtualMachineProfile vmProfile, final long nicI public Pair doInTransaction(final TransactionStatus status) { final NicVO nic = _nicDao.lockRow(nicId, true); if (nic == null) { - throw new ConcurrentOperationException(String.format("Unable to acquire lock on nic id=%d", nicId)); + throw new ConcurrentOperationException(String.format("Unable to acquire lock on NIC ID=%d", nicId)); } final Nic.State originalState = nic.getState(); @@ -2532,7 +2611,7 @@ public Pair doInTransaction(final TransactionStatus status) final Network network = networkToRelease.first(); final NicProfile profile = networkToRelease.second(); final List providersToImplement = getNetworkProviders(network.getId()); - for (final NetworkElement element : networkElements) { + for (final NetworkElement element : getNetworkElementsIncludingExtensions()) { if (providersToImplement.contains(element.getProvider())) { logger.debug("Asking {} to release {}", element.getName(), profile); //NOTE: Context appear to never be used in release method @@ -2545,7 +2624,7 @@ public Pair doInTransaction(final TransactionStatus status) @Override public void cleanupNics(final VirtualMachineProfile vm) { - logger.debug("Cleaning network for vm: {}", vm); + logger.debug("Cleaning Network for Instance: {}", vm); final List nics = _nicDao.listByVmId(vm.getId()); for (final NicVO nic : nics) { @@ -2566,7 +2645,7 @@ protected void removeNic(final VirtualMachineProfile vm, final NicVO nic) { try { releaseNic(vm, nic.getId()); } catch (final Exception ex) { - logger.warn("Failed to release nic: {} as part of remove operation due to", nic.toString(), ex); + logger.warn("Failed to release NIC: {} as part of remove operation due to", nic.toString(), ex); } } @@ -2595,15 +2674,15 @@ protected void removeNic(final VirtualMachineProfile vm, final NicVO nic) { */ if (nic.getReservationStrategy() == Nic.ReservationStrategy.Create) { final List providersToImplement = getNetworkProviders(network.getId()); - for (final NetworkElement element : networkElements) { + for (final NetworkElement element : getNetworkElementsIncludingExtensions()) { if (providersToImplement.contains(element.getProvider())) { logger.debug("Asking {} to release {}, according to the reservation strategy {}.", element.getName(), nic, nic.getReservationStrategy()); try { element.release(network, profile, vm, null); } catch (final ConcurrentOperationException ex) { - logger.warn("release failed during the nic " + nic.toString() + " removeNic due to ", ex); + logger.warn("Release failed during the NIC " + nic.toString() + " removeNic due to ", ex); } catch (final ResourceUnavailableException ex) { - logger.warn("release failed during the nic " + nic.toString() + " removeNic due to ", ex); + logger.warn("Release failed during the NIC " + nic.toString() + " removeNic due to ", ex); } } } @@ -2626,11 +2705,11 @@ && isDhcpAccrossMultipleSubnetsSupported(dhcpServiceProvider)) { if (dnsServiceProvider != null) { try { if (!dnsServiceProvider.removeDnsSupportForSubnet(network)) { - logger.warn("Failed to remove the ip alias on the dns server"); + logger.warn("Failed to remove the IP alias on the DNS server"); } } catch (final ResourceUnavailableException e) { //failed to remove the dnsconfig. - logger.info("Unable to delete the ip alias due to unable to contact the dns server."); + logger.info("Unable to delete the IP alias due to unable to contact the DNS server."); } } } @@ -2638,11 +2717,15 @@ && isDhcpAccrossMultipleSubnetsSupported(dhcpServiceProvider)) { final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName()); guru.deallocate(network, profile, vm); + if (nic.getReservationStrategy() == Nic.ReservationStrategy.Create) { + applyProfileToNicForRelease(nic, profile); + _nicDao.update(nic.getId(), nic); + } if (BooleanUtils.isNotTrue(preserveNics)) { _nicDao.remove(nic.getId()); } - logger.debug("Removed nic {}", nic); + logger.debug("Removed NIC ID=" + nic.getId()); // release assigned IPv6 for Isolated Network VR NIC if (Type.User.equals(vm.getType()) && GuestType.Isolated.equals(network.getGuestType()) @@ -2655,7 +2738,7 @@ && isDhcpAccrossMultipleSubnetsSupported(dhcpServiceProvider)) { //remove the secondary ip addresses corresponding to this nic if (!removeVmSecondaryIpsOfNic(nic.getId())) { - logger.debug("Removing nic {} secondary ip addresses failed", nic); + logger.debug("Removing NIC {} secondary IP addresses failed", nic); } } @@ -2694,12 +2777,12 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { } }); if (!dhcpServiceProvider.removeDhcpSupportForSubnet(network)) { - logger.warn("Failed to remove the ip alias on the router, marking it as removed in db and freed the allocated ip {}", ipAlias.getIp4Address()); + logger.warn("Failed to remove the IP alias on the router, marking it as removed in db and freed the allocated IP {}", ipAlias.getIp4Address()); } } } catch (final ResourceUnavailableException e) { //failed to remove the dhcpconfig on the router. - logger.info("Unable to delete the ip alias due to unable to contact the virtualrouter."); + logger.info("Unable to delete the IP alias due to unable to contact the virtualrouter."); } } @@ -2719,7 +2802,7 @@ public Network createPrivateNetwork(final long networkOfferingId, final String n return createGuestNetwork(networkOfferingId, name, displayText, gateway, cidr, vlanId, bypassVlanOverlapCheck, null, owner, null, pNtwk, pNtwk.getDataCenterId(), ACLType.Account, null, vpcId, null, null, true, null, null, null, true, null, null, - null, null, null, null, null, null); + null, null, null, null, null, null, true); } @Override @@ -2730,10 +2813,25 @@ public Network createGuestNetwork(final long networkOfferingId, final String nam final Boolean isDisplayNetworkEnabled, final String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId, String routerIp, String routerIpv6, String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, Pair vrIfaceMTUs, Integer networkCidrSize) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException { + return createGuestNetwork(networkOfferingId, name, displayText, gateway, cidr, vlanId, bypassVlanOverlapCheck, + networkDomain, owner, domainId, pNtwk, zoneId, aclType, subdomainAccess, vpcId, ip6Gateway, ip6Cidr, + isDisplayNetworkEnabled, isolatedPvlan, isolatedPvlanType, externalId, false, routerIp, routerIpv6, + ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2, vrIfaceMTUs, networkCidrSize, true); + } + + @Override + @DB + public Network createGuestNetwork(final long networkOfferingId, final String name, final String displayText, final String gateway, final String cidr, String vlanId, + boolean bypassVlanOverlapCheck, String networkDomain, final Account owner, final Long domainId, final PhysicalNetwork pNtwk, + final long zoneId, final ACLType aclType, Boolean subdomainAccess, final Long vpcId, final String ip6Gateway, final String ip6Cidr, + final Boolean isDisplayNetworkEnabled, final String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId, + String routerIp, String routerIpv6, String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, + Pair vrIfaceMTUs, Integer networkCidrSize, boolean keepMacAddressOnPublicNic) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException { // create Isolated/Shared/L2 network return createGuestNetwork(networkOfferingId, name, displayText, gateway, cidr, vlanId, bypassVlanOverlapCheck, networkDomain, owner, domainId, pNtwk, zoneId, aclType, subdomainAccess, vpcId, ip6Gateway, ip6Cidr, - isDisplayNetworkEnabled, isolatedPvlan, isolatedPvlanType, externalId, false, routerIp, routerIpv6, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2, vrIfaceMTUs, networkCidrSize); + isDisplayNetworkEnabled, isolatedPvlan, isolatedPvlanType, externalId, false, routerIp, routerIpv6, + ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2, vrIfaceMTUs, networkCidrSize, keepMacAddressOnPublicNic); } @DB @@ -2742,7 +2840,8 @@ private Network createGuestNetwork(final long networkOfferingId, final String na final long zoneId, final ACLType aclType, Boolean subdomainAccess, final Long vpcId, final String ip6Gateway, final String ip6Cidr, final Boolean isDisplayNetworkEnabled, final String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId, final Boolean isPrivateNetwork, String routerIp, String routerIpv6, final String ip4Dns1, final String ip4Dns2, - final String ip6Dns1, final String ip6Dns2, Pair vrIfaceMTUs, Integer networkCidrSize) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException { + final String ip6Dns1, final String ip6Dns2, Pair vrIfaceMTUs, Integer networkCidrSize, + boolean keepMacAddressOnPublicNic) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException { final NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId); final DataCenterVO zone = _dcDao.findById(zoneId); @@ -2752,12 +2851,6 @@ private Network createGuestNetwork(final long networkOfferingId, final String na return null; } - final boolean updateResourceCount = resourceCountNeedsUpdate(ntwkOff, aclType); - //check resource limits - if (updateResourceCount) { - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.network, isDisplayNetworkEnabled); - } - // Validate network offering if (ntwkOff.getState() != NetworkOffering.State.Enabled) { // see NetworkOfferingVO @@ -2776,223 +2869,224 @@ private Network createGuestNetwork(final long networkOfferingId, final String na boolean ipv6 = false; - if (StringUtils.isNoneBlank(ip6Gateway, ip6Cidr)) { - ipv6 = true; - } - // Validate zone - if (zone.getNetworkType() == NetworkType.Basic) { - // In Basic zone the network should have aclType=Domain, domainId=1, subdomainAccess=true - if (aclType == null || aclType != ACLType.Domain) { - throw new InvalidParameterValueException("Only AclType=Domain can be specified for network creation in Basic zone"); - } - - // Only one guest network is supported in Basic zone - final List guestNetworks = _networksDao.listByZoneAndTrafficType(zone.getId(), TrafficType.Guest); - if (!guestNetworks.isEmpty()) { - throw new InvalidParameterValueException("Can't have more than one Guest network in zone with network type " + NetworkType.Basic); + try (CheckedReservation networkReservation = new CheckedReservation(owner, domainId, Resource.ResourceType.network, null, null, 1L, reservationDao, _resourceLimitMgr)) { + if (StringUtils.isNoneBlank(ip6Gateway, ip6Cidr)) { + ipv6 = true; } + // Validate zone + if (zone.getNetworkType() == NetworkType.Basic) { + // In Basic zone the network should have aclType=Domain, domainId=1, subdomainAccess=true + if (aclType == null || aclType != ACLType.Domain) { + throw new InvalidParameterValueException("Only AclType=Domain can be specified for network creation in Basic zone"); + } - // if zone is basic, only Shared network offerings w/o source nat service are allowed - if (!(ntwkOff.getGuestType() == GuestType.Shared && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat))) { - throw new InvalidParameterValueException("For zone of type " + NetworkType.Basic + " only offerings of " + "guestType " + GuestType.Shared + " with disabled " - + Service.SourceNat.getName() + " service are allowed"); - } + // Only one guest network is supported in Basic zone + final List guestNetworks = _networksDao.listByZoneAndTrafficType(zone.getId(), TrafficType.Guest); + if (!guestNetworks.isEmpty()) { + throw new InvalidParameterValueException("Can't have more than one Guest network in zone with network type " + NetworkType.Basic); + } - if (domainId == null || domainId != Domain.ROOT_DOMAIN) { - throw new InvalidParameterValueException("Guest network in Basic zone should be dedicated to ROOT domain"); - } + // if zone is basic, only Shared network offerings w/o source nat service are allowed + if (!(ntwkOff.getGuestType() == GuestType.Shared && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat))) { + throw new InvalidParameterValueException("For zone of type " + NetworkType.Basic + " only offerings of " + "guestType " + GuestType.Shared + " with disabled " + + Service.SourceNat.getName() + " service are allowed"); + } - if (subdomainAccess == null) { - subdomainAccess = true; - } else if (!subdomainAccess) { - throw new InvalidParameterValueException("Subdomain access should be set to true for the" + " guest network in the Basic zone"); - } + if (domainId == null || domainId != Domain.ROOT_DOMAIN) { + throw new InvalidParameterValueException("Guest network in Basic zone should be dedicated to ROOT domain"); + } - if (vlanId == null) { - vlanId = Vlan.UNTAGGED; - } else { - if (!vlanId.equalsIgnoreCase(Vlan.UNTAGGED)) { - throw new InvalidParameterValueException("Only vlan " + Vlan.UNTAGGED + " can be created in " + "the zone of type " + NetworkType.Basic); + if (subdomainAccess == null) { + subdomainAccess = true; + } else if (!subdomainAccess) { + throw new InvalidParameterValueException("Subdomain access should be set to true for the" + " guest network in the Basic zone"); } - } - } else if (zone.getNetworkType() == NetworkType.Advanced) { - if (zone.isSecurityGroupEnabled()) { - if (isolatedPvlan != null) { - throw new InvalidParameterValueException("Isolated Private VLAN is not supported with security group!"); + if (vlanId == null) { + vlanId = Vlan.UNTAGGED; + } else { + if (!vlanId.equalsIgnoreCase(Vlan.UNTAGGED)) { + throw new InvalidParameterValueException("Only vlan " + Vlan.UNTAGGED + " can be created in " + "the zone of type " + NetworkType.Basic); + } } - // Only Account specific Isolated network with sourceNat service disabled are allowed in security group - // enabled zone - if ((ntwkOff.getGuestType() != GuestType.Shared) && (ntwkOff.getGuestType() != GuestType.L2)) { - throw new InvalidParameterValueException("Only shared or L2 guest network can be created in security group enabled zone"); + + } else if (zone.getNetworkType() == NetworkType.Advanced) { + if (zone.isSecurityGroupEnabled()) { + if (isolatedPvlan != null) { + throw new InvalidParameterValueException("Isolated Private VLAN is not supported with security group!"); + } + // Only Account specific Isolated network with sourceNat service disabled are allowed in security group + // enabled zone + if ((ntwkOff.getGuestType() != GuestType.Shared) && (ntwkOff.getGuestType() != GuestType.L2)) { + throw new InvalidParameterValueException("Only shared or L2 guest network can be created in security group enabled zone"); + } + if (_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat)) { + throw new InvalidParameterValueException("Service SourceNat is not allowed in security group enabled zone"); + } } - if (_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat)) { - throw new InvalidParameterValueException("Service SourceNat is not allowed in security group enabled zone"); + + //don't allow eip/elb networks in Advance zone + if (ntwkOff.isElasticIp() || ntwkOff.isElasticLb()) { + throw new InvalidParameterValueException("Elastic IP and Elastic LB services are supported in zone of type " + NetworkType.Basic); } } - //don't allow eip/elb networks in Advance zone - if (ntwkOff.isElasticIp() || ntwkOff.isElasticLb()) { - throw new InvalidParameterValueException("Elastic IP and Elastic LB services are supported in zone of type " + NetworkType.Basic); + if (ipv6 && !GuestType.Shared.equals(ntwkOff.getGuestType())) { + _networkModel.checkIp6CidrSizeEqualTo64(ip6Cidr); } - } - if (ipv6 && !GuestType.Shared.equals(ntwkOff.getGuestType())) { - _networkModel.checkIp6CidrSizeEqualTo64(ip6Cidr); - } - - //TODO(VXLAN): Support VNI specified - // VlanId can be specified only when network offering supports it - final boolean vlanSpecified = vlanId != null; - if (vlanSpecified != ntwkOff.isSpecifyVlan()) { - if (vlanSpecified) { - if (!isSharedNetworkWithoutSpecifyVlan(ntwkOff) && !isPrivateGatewayWithoutSpecifyVlan(ntwkOff)) { - throw new InvalidParameterValueException("Can't specify vlan; corresponding offering says specifyVlan=false"); + //TODO(VXLAN): Support VNI specified + // VlanId can be specified only when network offering supports it + final boolean vlanSpecified = vlanId != null; + if (vlanSpecified != ntwkOff.isSpecifyVlan()) { + if (vlanSpecified) { + if (!isSharedNetworkWithoutSpecifyVlan(ntwkOff) && !isPrivateGatewayWithoutSpecifyVlan(ntwkOff)) { + throw new InvalidParameterValueException("Can't specify vlan; corresponding offering says specifyVlan=false"); + } + } else { + throw new InvalidParameterValueException("Vlan has to be specified; corresponding offering says specifyVlan=true"); } - } else { - throw new InvalidParameterValueException("Vlan has to be specified; corresponding offering says specifyVlan=true"); } - } - if (vlanSpecified) { - URI uri = encodeVlanIdIntoBroadcastUri(vlanId, pNtwk); - // Aux: generate secondary URI for secondary VLAN ID (if provided) for performing checks - URI secondaryUri = StringUtils.isNotBlank(isolatedPvlan) ? BroadcastDomainType.fromString(isolatedPvlan) : null; - if (isSharedNetworkWithoutSpecifyVlan(ntwkOff) || isPrivateGatewayWithoutSpecifyVlan(ntwkOff)) { - bypassVlanOverlapCheck = true; - } - //don't allow to specify vlan tag used by physical network for dynamic vlan allocation - if (!(bypassVlanOverlapCheck && (ntwkOff.getGuestType() == GuestType.Shared || isPrivateNetwork)) - && _dcDao.findVnet(zoneId, pNtwk.getId(), BroadcastDomainType.getValue(uri)).size() > 0) { - throw new InvalidParameterValueException("The VLAN tag to use for new guest network, " + vlanId + " is already being used for dynamic vlan allocation for the guest network in zone " - + zone.getName()); - } - if (secondaryUri != null && !(bypassVlanOverlapCheck && ntwkOff.getGuestType() == GuestType.Shared) && - _dcDao.findVnet(zoneId, pNtwk.getId(), BroadcastDomainType.getValue(secondaryUri)).size() > 0) { - throw new InvalidParameterValueException(String.format( - "The VLAN tag for isolated PVLAN %s is already being used for dynamic vlan allocation for the guest network in zone %s", - isolatedPvlan, zone)); - } - if (!UuidUtils.isUuid(vlanId)) { - // For Isolated and L2 networks, don't allow to create network with vlan that already exists in the zone - if (!hasGuestBypassVlanOverlapCheck(bypassVlanOverlapCheck, ntwkOff, isPrivateNetwork)) { - if (_networksDao.listByZoneAndUriAndGuestType(zoneId, uri.toString(), null).size() > 0) { - throw new InvalidParameterValueException(String.format( - "Network with vlan %s already exists or overlaps with other network vlans in zone %s", - vlanId, zone)); - } else if (secondaryUri != null && _networksDao.listByZoneAndUriAndGuestType(zoneId, secondaryUri.toString(), null).size() > 0) { - throw new InvalidParameterValueException(String.format( - "Network with vlan %s already exists or overlaps with other network vlans in zone %s", - isolatedPvlan, zone)); - } else { - final List dcVnets = _datacenterVnetDao.findVnet(zoneId, BroadcastDomainType.getValue(uri)); - //for the network that is created as part of private gateway, - //the vnet is not coming from the data center vnet table, so the list can be empty - if (!dcVnets.isEmpty()) { - final DataCenterVnetVO dcVnet = dcVnets.get(0); - // Fail network creation if specified vlan is dedicated to a different account - if (dcVnet.getAccountGuestVlanMapId() != null) { - final Long accountGuestVlanMapId = dcVnet.getAccountGuestVlanMapId(); - final AccountGuestVlanMapVO map = _accountGuestVlanMapDao.findById(accountGuestVlanMapId); - if (map.getAccountId() != owner.getAccountId()) { - throw new InvalidParameterValueException("Vlan " + vlanId + " is dedicated to a different account"); - } - // Fail network creation if owner has a dedicated range of vlans but the specified vlan belongs to the system pool - } else { - final List maps = _accountGuestVlanMapDao.listAccountGuestVlanMapsByAccount(owner.getAccountId()); - if (maps != null && !maps.isEmpty()) { - final int vnetsAllocatedToAccount = _datacenterVnetDao.countVnetsAllocatedToAccount(zoneId, owner.getAccountId()); - final int vnetsDedicatedToAccount = _datacenterVnetDao.countVnetsDedicatedToAccount(zoneId, owner.getAccountId()); - if (vnetsAllocatedToAccount < vnetsDedicatedToAccount) { - throw new InvalidParameterValueException("Specified vlan " + vlanId + " doesn't belong" + " to the vlan range dedicated to the owner " - + owner.getAccountName()); + if (vlanSpecified) { + URI uri = encodeVlanIdIntoBroadcastUri(vlanId, pNtwk); + // Aux: generate secondary URI for secondary VLAN ID (if provided) for performing checks + URI secondaryUri = StringUtils.isNotBlank(isolatedPvlan) ? BroadcastDomainType.fromString(isolatedPvlan) : null; + if (isSharedNetworkWithoutSpecifyVlan(ntwkOff) || isPrivateGatewayWithoutSpecifyVlan(ntwkOff)) { + bypassVlanOverlapCheck = true; + } + //don't allow to specify vlan tag used by physical network for dynamic vlan allocation + if (!(bypassVlanOverlapCheck && (ntwkOff.getGuestType() == GuestType.Shared || isPrivateNetwork)) + && _dcDao.findVnet(zoneId, pNtwk.getId(), BroadcastDomainType.getValue(uri)).size() > 0) { + throw new InvalidParameterValueException("The VLAN tag to use for new guest network, " + vlanId + " is already being used for dynamic vlan allocation for the guest network in zone " + + zone.getName()); + } + if (secondaryUri != null && !(bypassVlanOverlapCheck && ntwkOff.getGuestType() == GuestType.Shared) && + _dcDao.findVnet(zoneId, pNtwk.getId(), BroadcastDomainType.getValue(secondaryUri)).size() > 0) { + throw new InvalidParameterValueException(String.format( + "The VLAN tag for isolated PVLAN %s is already being used for dynamic vlan allocation for the guest network in zone %s", + isolatedPvlan, zone)); + } + if (!UuidUtils.isUuid(vlanId)) { + // For Isolated and L2 networks, don't allow to create network with vlan that already exists in the zone + if (!hasGuestBypassVlanOverlapCheck(bypassVlanOverlapCheck, ntwkOff, isPrivateNetwork)) { + if (_networksDao.listByZoneAndUriAndGuestType(zoneId, uri.toString(), null).size() > 0) { + throw new InvalidParameterValueException(String.format( + "Network with vlan %s already exists or overlaps with other network vlans in zone %s", + vlanId, zone)); + } else if (secondaryUri != null && _networksDao.listByZoneAndUriAndGuestType(zoneId, secondaryUri.toString(), null).size() > 0) { + throw new InvalidParameterValueException(String.format( + "Network with vlan %s already exists or overlaps with other network vlans in zone %s", + isolatedPvlan, zone)); + } else { + final List dcVnets = _datacenterVnetDao.findVnet(zoneId, BroadcastDomainType.getValue(uri)); + //for the network that is created as part of private gateway, + //the vnet is not coming from the data center vnet table, so the list can be empty + if (!dcVnets.isEmpty()) { + final DataCenterVnetVO dcVnet = dcVnets.get(0); + // Fail network creation if specified vlan is dedicated to a different account + if (dcVnet.getAccountGuestVlanMapId() != null) { + final Long accountGuestVlanMapId = dcVnet.getAccountGuestVlanMapId(); + final AccountGuestVlanMapVO map = _accountGuestVlanMapDao.findById(accountGuestVlanMapId); + if (map.getAccountId() != owner.getAccountId()) { + throw new InvalidParameterValueException("Vlan " + vlanId + " is dedicated to a different account"); + } + // Fail network creation if owner has a dedicated range of vlans but the specified vlan belongs to the system pool + } else { + final List maps = _accountGuestVlanMapDao.listAccountGuestVlanMapsByAccount(owner.getAccountId()); + if (maps != null && !maps.isEmpty()) { + final int vnetsAllocatedToAccount = _datacenterVnetDao.countVnetsAllocatedToAccount(zoneId, owner.getAccountId()); + final int vnetsDedicatedToAccount = _datacenterVnetDao.countVnetsDedicatedToAccount(zoneId, owner.getAccountId()); + if (vnetsAllocatedToAccount < vnetsDedicatedToAccount) { + throw new InvalidParameterValueException("Specified vlan " + vlanId + " doesn't belong" + " to the vlan range dedicated to the owner " + + owner.getAccountName()); + } } } } } - } - } else { - // don't allow to creating shared network with given Vlan ID, if there already exists a isolated network or - // shared network with same Vlan ID in the zone - if (!bypassVlanOverlapCheck && _networksDao.listByZoneAndUriAndGuestType(zoneId, uri.toString(), GuestType.Isolated).size() > 0) { - throw new InvalidParameterValueException(String.format( - "There is an existing isolated/shared network that overlaps with vlan id:%s in zone %s", vlanId, zone)); + } else { + // don't allow to creating shared network with given Vlan ID, if there already exists a isolated network or + // shared network with same Vlan ID in the zone + if (!bypassVlanOverlapCheck && _networksDao.listByZoneAndUriAndGuestType(zoneId, uri.toString(), GuestType.Isolated).size() > 0) { + throw new InvalidParameterValueException(String.format( + "There is an existing isolated/shared network that overlaps with vlan id:%s in zone %s", vlanId, zone)); + } } } - } - } + } - // If networkDomain is not specified, take it from the global configuration - if (_networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.Dns)) { - final Map dnsCapabilities = _networkModel.getNetworkOfferingServiceCapabilities(_entityMgr.findById(NetworkOffering.class, networkOfferingId), - Service.Dns); - final String isUpdateDnsSupported = dnsCapabilities.get(Capability.AllowDnsSuffixModification); - if (isUpdateDnsSupported == null || !Boolean.valueOf(isUpdateDnsSupported)) { - if (networkDomain != null) { - // TBD: NetworkOfferingId and zoneId. Send uuids instead. - throw new InvalidParameterValueException(String.format( - "Domain name change is not supported by network offering id=%d in zone %s", - networkOfferingId, zone)); - } - } else { - if (networkDomain == null) { - // 1) Get networkDomain from the corresponding account/domain/zone - if (aclType == ACLType.Domain) { - networkDomain = _networkModel.getDomainNetworkDomain(domainId, zoneId); - } else if (aclType == ACLType.Account) { - networkDomain = _networkModel.getAccountNetworkDomain(owner.getId(), zoneId); + // If networkDomain is not specified, take it from the global configuration + if (_networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.Dns)) { + final Map dnsCapabilities = _networkModel.getNetworkOfferingServiceCapabilities(_entityMgr.findById(NetworkOffering.class, networkOfferingId), + Service.Dns); + final String isUpdateDnsSupported = dnsCapabilities.get(Capability.AllowDnsSuffixModification); + if (isUpdateDnsSupported == null || !Boolean.valueOf(isUpdateDnsSupported)) { + if (networkDomain != null) { + // TBD: NetworkOfferingId and zoneId. Send uuids instead. + throw new InvalidParameterValueException(String.format( + "Domain name change is not supported by network offering id=%d in zone %s", + networkOfferingId, zone)); } - - // 2) If null, generate networkDomain using domain suffix from the global config variables + } else { if (networkDomain == null) { - networkDomain = "cs" + Long.toHexString(owner.getId()) + GuestDomainSuffix.valueIn(zoneId); - } + // 1) Get networkDomain from the corresponding account/domain/zone + if (aclType == ACLType.Domain) { + networkDomain = _networkModel.getDomainNetworkDomain(domainId, zoneId); + } else if (aclType == ACLType.Account) { + networkDomain = _networkModel.getAccountNetworkDomain(owner.getId(), zoneId); + } - } else { - // validate network domain - if (!NetUtils.verifyDomainName(networkDomain)) { - throw new InvalidParameterValueException("Invalid network domain. Total length shouldn't exceed 190 chars. Each domain " - + "label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " - + "and the hyphen ('-'); can't start or end with \"-\""); + // 2) If null, generate networkDomain using domain suffix from the global config variables + if (networkDomain == null) { + networkDomain = "cs" + Long.toHexString(owner.getId()) + GuestDomainSuffix.valueIn(zoneId); + } + + } else { + // validate network domain + if (!NetUtils.verifyDomainName(networkDomain)) { + throw new InvalidParameterValueException("Invalid network domain. Total length shouldn't exceed 190 chars. Each domain " + + "label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " + + "and the hyphen ('-'); can't start or end with \"-\""); + } } } } - } - // In Advance zone Cidr for Shared networks and Isolated networks w/o source nat service can't be NULL - 2.2.x - // limitation, remove after we introduce support for multiple ip ranges - // with different Cidrs for the same Shared network - final boolean cidrRequired = zone.getNetworkType() == NetworkType.Advanced - && ntwkOff.getTrafficType() == TrafficType.Guest - && (ntwkOff.getGuestType() == GuestType.Shared || (ntwkOff.getGuestType() == GuestType.Isolated - && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat) - && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.Gateway))); - if (cidr == null && ip6Cidr == null && cidrRequired) { - if (ntwkOff.getGuestType() == GuestType.Shared) { - throw new InvalidParameterValueException(String.format("Gateway/netmask are required when creating %s networks.", Network.GuestType.Shared)); - } else { - throw new InvalidParameterValueException("gateway/netmask are required when create network of" + " type " + GuestType.Isolated + " with service " + Service.SourceNat.getName() + " disabled"); + // In Advance zone Cidr for Shared networks and Isolated networks w/o source nat service can't be NULL - 2.2.x + // limitation, remove after we introduce support for multiple ip ranges + // with different Cidrs for the same Shared network + final boolean cidrRequired = zone.getNetworkType() == NetworkType.Advanced + && ntwkOff.getTrafficType() == TrafficType.Guest + && (ntwkOff.getGuestType() == GuestType.Shared || (ntwkOff.getGuestType() == GuestType.Isolated + && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat) + && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.Gateway))); + if (cidr == null && ip6Cidr == null && cidrRequired) { + if (ntwkOff.getGuestType() == GuestType.Shared) { + throw new InvalidParameterValueException(String.format("Gateway/netmask are required when creating %s networks.", Network.GuestType.Shared)); + } else { + throw new InvalidParameterValueException("gateway/netmask are required when create network of" + " type " + GuestType.Isolated + " with service " + Service.SourceNat.getName() + " disabled"); + } } - } - checkL2OfferingServices(ntwkOff); + checkL2OfferingServices(ntwkOff); - // No cidr can be specified in Basic zone - if (zone.getNetworkType() == NetworkType.Basic && cidr != null) { - throw new InvalidParameterValueException("StartIp/endIp/gateway/netmask can't be specified for zone of type " + NetworkType.Basic); - } + // No cidr can be specified in Basic zone + if (zone.getNetworkType() == NetworkType.Basic && cidr != null) { + throw new InvalidParameterValueException("StartIp/endIp/gateway/netmask can't be specified for zone of type " + NetworkType.Basic); + } - // Check if cidr is RFC1918 compliant if the network is Guest Isolated for IPv4 - if (cidr != null && (ntwkOff.getGuestType() == Network.GuestType.Isolated && ntwkOff.getTrafficType() == TrafficType.Guest) && - !NetUtils.validateGuestCidr(cidr, !ConfigurationManager.AllowNonRFC1918CompliantIPs.value())) { + // Check if cidr is RFC1918 compliant if the network is Guest Isolated for IPv4 + if (cidr != null && (ntwkOff.getGuestType() == Network.GuestType.Isolated && ntwkOff.getTrafficType() == TrafficType.Guest) && + !NetUtils.validateGuestCidr(cidr, !ConfigurationManager.AllowNonRFC1918CompliantIPs.value())) { throw new InvalidParameterValueException("Virtual Guest Cidr " + cidr + " is not RFC 1918 or 6598 compliant"); - } + } final String networkDomainFinal = networkDomain; final String vlanIdFinal = vlanId; final Boolean subdomainAccessFinal = subdomainAccess; - final Network network = Transaction.execute(new TransactionCallback() { + final Network network = Transaction.execute(new TransactionCallback<>() { @Override public Network doInTransaction(final TransactionStatus status) { Long physicalNetworkId = null; @@ -3003,75 +3097,75 @@ public Network doInTransaction(final TransactionStatus status) { final NetworkVO userNetwork = new NetworkVO(); userNetwork.setNetworkDomain(networkDomainFinal); - if (cidr != null && gateway != null) { - userNetwork.setCidr(cidr); - userNetwork.setGateway(gateway); - } - - if (StringUtils.isNoneBlank(ip6Gateway, ip6Cidr)) { - userNetwork.setIp6Cidr(ip6Cidr); - userNetwork.setIp6Gateway(ip6Gateway); - } + if (cidr != null && gateway != null) { + userNetwork.setCidr(cidr); + userNetwork.setGateway(gateway); + } - if (externalId != null) { - userNetwork.setExternalId(externalId); - } + if (StringUtils.isNoneBlank(ip6Gateway, ip6Cidr)) { + userNetwork.setIp6Cidr(ip6Cidr); + userNetwork.setIp6Gateway(ip6Gateway); + } - if (StringUtils.isNotBlank(routerIp)) { - userNetwork.setRouterIp(routerIp); - } + if (externalId != null) { + userNetwork.setExternalId(externalId); + } - if (StringUtils.isNotBlank(routerIpv6)) { - userNetwork.setRouterIpv6(routerIpv6); - } + if (StringUtils.isNotBlank(routerIp)) { + userNetwork.setRouterIp(routerIp); + } - if (vrIfaceMTUs != null) { - if (vrIfaceMTUs.first() != null && vrIfaceMTUs.first() > 0) { - userNetwork.setPublicMtu(vrIfaceMTUs.first()); - } else { - userNetwork.setPublicMtu(Integer.valueOf(NetworkService.VRPublicInterfaceMtu.defaultValue())); + if (StringUtils.isNotBlank(routerIpv6)) { + userNetwork.setRouterIpv6(routerIpv6); } - if (vrIfaceMTUs.second() != null && vrIfaceMTUs.second() > 0) { - userNetwork.setPrivateMtu(vrIfaceMTUs.second()); + if (vrIfaceMTUs != null) { + if (vrIfaceMTUs.first() != null && vrIfaceMTUs.first() > 0) { + userNetwork.setPublicMtu(vrIfaceMTUs.first()); + } else { + userNetwork.setPublicMtu(Integer.valueOf(NetworkService.VRPublicInterfaceMtu.defaultValue())); + } + + if (vrIfaceMTUs.second() != null && vrIfaceMTUs.second() > 0) { + userNetwork.setPrivateMtu(vrIfaceMTUs.second()); + } else { + userNetwork.setPrivateMtu(Integer.valueOf(NetworkService.VRPrivateInterfaceMtu.defaultValue())); + } } else { + userNetwork.setPublicMtu(Integer.valueOf(NetworkService.VRPublicInterfaceMtu.defaultValue())); userNetwork.setPrivateMtu(Integer.valueOf(NetworkService.VRPrivateInterfaceMtu.defaultValue())); } - } else { - userNetwork.setPublicMtu(Integer.valueOf(NetworkService.VRPublicInterfaceMtu.defaultValue())); - userNetwork.setPrivateMtu(Integer.valueOf(NetworkService.VRPrivateInterfaceMtu.defaultValue())); - } - if (!GuestType.L2.equals(userNetwork.getGuestType())) { - if (StringUtils.isNotBlank(ip4Dns1)) { - userNetwork.setDns1(ip4Dns1); - } - if (StringUtils.isNotBlank(ip4Dns2)) { - userNetwork.setDns2(ip4Dns2); - } - if (StringUtils.isNotBlank(ip6Dns1)) { - userNetwork.setIp6Dns1(ip6Dns1); - } - if (StringUtils.isNotBlank(ip6Dns2)) { - userNetwork.setIp6Dns2(ip6Dns2); + if (!GuestType.L2.equals(userNetwork.getGuestType())) { + if (StringUtils.isNotBlank(ip4Dns1)) { + userNetwork.setDns1(ip4Dns1); + } + if (StringUtils.isNotBlank(ip4Dns2)) { + userNetwork.setDns2(ip4Dns2); + } + if (StringUtils.isNotBlank(ip6Dns1)) { + userNetwork.setIp6Dns1(ip6Dns1); + } + if (StringUtils.isNotBlank(ip6Dns2)) { + userNetwork.setIp6Dns2(ip6Dns2); + } } - } - if (vlanIdFinal != null) { - if (isolatedPvlan == null) { - URI uri = null; - if (UuidUtils.isUuid(vlanIdFinal)) { - //Logical router's UUID provided as VLAN_ID - userNetwork.setVlanIdAsUUID(vlanIdFinal); //Set transient field - } else { - uri = encodeVlanIdIntoBroadcastUri(vlanIdFinal, pNtwk); - } + if (vlanIdFinal != null) { + if (isolatedPvlan == null) { + URI uri = null; + if (UuidUtils.isUuid(vlanIdFinal)) { + //Logical router's UUID provided as VLAN_ID + userNetwork.setVlanIdAsUUID(vlanIdFinal); //Set transient field + } else { + uri = encodeVlanIdIntoBroadcastUri(vlanIdFinal, pNtwk); + } - if (_networksDao.listByPhysicalNetworkPvlan(physicalNetworkId, uri.toString()).size() > 0) { - throw new InvalidParameterValueException(String.format( - "Network with vlan %s already exists or overlaps with other network pvlans in zone %s", - vlanIdFinal, zone)); - } + if (_networksDao.listByPhysicalNetworkPvlan(physicalNetworkId, uri.toString()).size() > 0) { + throw new InvalidParameterValueException(String.format( + "Network with vlan %s already exists or overlaps with other network pvlans in zone %s", + vlanIdFinal, zone)); + } userNetwork.setBroadcastUri(uri); if (!vlanIdFinal.equalsIgnoreCase(Vlan.UNTAGGED)) { @@ -3095,9 +3189,10 @@ public Network doInTransaction(final TransactionStatus status) { } } userNetwork.setNetworkCidrSize(networkCidrSize); + userNetwork.setKeepMacAddressOnPublicNic(keepMacAddressOnPublicNic); final List networks = setupNetwork(owner, ntwkOff, userNetwork, plan, name, displayText, true, domainId, aclType, subdomainAccessFinal, vpcId, isDisplayNetworkEnabled); - Network network = null; + Network network; if (networks == null || networks.isEmpty()) { throw new CloudRuntimeException("Fail to create a network"); } else { @@ -3115,8 +3210,8 @@ public Network doInTransaction(final TransactionStatus status) { } } - if (updateResourceCount) { - _resourceLimitMgr.incrementResourceCount(owner.getId(), ResourceType.network, isDisplayNetworkEnabled); + if (isResourceCountUpdateNeeded(ntwkOff)) { + changeAccountResourceCountOrRecalculateDomainResourceCount(owner.getAccountId(), domainId, isDisplayNetworkEnabled, true); } UsageEventUtils.publishNetworkCreation(network); @@ -3124,9 +3219,10 @@ public Network doInTransaction(final TransactionStatus status) { } }); - CallContext.current().setEventDetails("Network Id: " + network.getId()); + CallContext.current().setEventDetails("Network ID: " + network.getUuid()); CallContext.current().putContextParameter(Network.class, network.getUuid()); return network; + } } @Override @@ -3226,10 +3322,10 @@ public boolean shutdownNetwork(final long networkId, final ReservationContext co final boolean success = shutdownNetworkElementsAndResources(context, cleanupElements, network); final NetworkVO networkFinal = network; - final boolean result = Transaction.execute(new TransactionCallback() { + final boolean result = Transaction.execute(new TransactionCallback<>() { @Override public Boolean doInTransaction(final TransactionStatus status) { - boolean result = false; + boolean result; if (success) { logger.debug("Network {} is shutdown successfully, cleaning up corresponding resources now.", networkFinal); @@ -3309,7 +3405,7 @@ public boolean shutdownNetworkElementsAndResources(final ReservationContext cont // 2) Shutdown all the network elements boolean success = true; - for (final NetworkElement element : networkElements) { + for (final NetworkElement element : getNetworkElementsIncludingExtensions()) { if (providersToShutdown.contains(element.getProvider())) { try { logger.debug("Sending network shutdown to {}", element.getName()); @@ -3376,7 +3472,7 @@ public boolean destroyNetwork(final long networkId, final ReservationContext con for (final UserVmVO vm : userVms) { if (!(vm.getState() == VirtualMachine.State.Expunging && vm.getRemoved() != null)) { - logger.warn("Can't delete the network, not all user vms are expunged. Vm {} is in {} state", vm, vm.getState()); + logger.warn("Can't delete the Network, not all User Instances are expunged. Instance {} is in {} state", vm, vm.getState()); return false; } } @@ -3384,7 +3480,7 @@ public boolean destroyNetwork(final long networkId, final ReservationContext con // Don't allow to delete network via api call when it has vms assigned to it final int nicCount = getActiveNicsInNetwork(networkId); if (nicCount > 0) { - logger.debug("The network {} has active Nics, but shouldn't.", network); + logger.debug("The Network {} has active NICs, but shouldn't.", network); // at this point we have already determined that there are no active user vms in network // if the op_networks table shows active nics, it's a bug in releasing nics updating op_networks _networksDao.changeActiveNicsBy(networkId, -1 * nicCount); @@ -3395,7 +3491,7 @@ public boolean destroyNetwork(final long networkId, final ReservationContext con if (zone.getNetworkType() == NetworkType.Basic) { final List systemVms = _vmDao.listNonRemovedVmsByTypeAndNetwork(network.getId(), Type.ConsoleProxy, Type.SecondaryStorageVm); if (systemVms != null && !systemVms.isEmpty()) { - logger.warn("Can't delete the network, not all consoleProxy/secondaryStorage vms are expunged"); + logger.warn("Can't delete the Network, not all consoleProxy/secondaryStorage VMs are expunged"); return false; } } @@ -3420,7 +3516,7 @@ public boolean destroyNetwork(final long networkId, final ReservationContext con // get providers to destroy final List providersToDestroy = getNetworkProviders(network.getId()); - for (final NetworkElement element : networkElements) { + for (final NetworkElement element : getNetworkElementsIncludingExtensions()) { if (providersToDestroy.contains(element.getProvider())) { try { logger.debug("Sending destroy to {}", element); @@ -3447,18 +3543,18 @@ public boolean destroyNetwork(final long networkId, final ReservationContext con final NetworkVO networkFinal = network; try { - final List deletedVlanRangeToPublish = Transaction.execute(new TransactionCallback>() { + final List deletedVlanRangeToPublish = Transaction.execute(new TransactionCallback<>() { @Override public List doInTransaction(TransactionStatus status) { final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, networkFinal.getGuruName()); if (!guru.trash(networkFinal, _networkOfferingDao.findById(networkFinal.getNetworkOfferingId()))) { - throw new CloudRuntimeException("Failed to trash network."); + throw new CloudRuntimeException("Failed to trash Network."); } Pair> deletedVlans = deleteVlansInNetwork(networkFinal, context.getCaller().getId(), callerAccount); if (!deletedVlans.first()) { - logger.warn("Failed to delete network " + networkFinal + "; was unable to cleanup corresponding ip ranges"); - throw new CloudRuntimeException("Failed to delete network " + networkFinal + "; was unable to cleanup corresponding ip ranges"); + logger.warn("Failed to delete Network " + networkFinal + "; was unable to cleanup corresponding IP ranges"); + throw new CloudRuntimeException("Failed to delete Network " + networkFinal + "; was unable to cleanup corresponding IP ranges"); } else { // commit transaction only when ips and vlans for the network are released successfully @@ -3492,9 +3588,8 @@ public List doInTransaction(TransactionStatus status) { } final NetworkOffering ntwkOff = _entityMgr.findById(NetworkOffering.class, networkFinal.getNetworkOfferingId()); - final boolean updateResourceCount = resourceCountNeedsUpdate(ntwkOff, networkFinal.getAclType()); - if (updateResourceCount) { - _resourceLimitMgr.decrementResourceCount(networkFinal.getAccountId(), ResourceType.network, networkFinal.getDisplayNetwork()); + if (isResourceCountUpdateNeeded(ntwkOff)) { + changeAccountResourceCountOrRecalculateDomainResourceCount(networkFinal.getAccountId(), networkFinal.getDomainId(), networkFinal.getDisplayNetwork(), false); } } return deletedVlans.second(); @@ -3503,7 +3598,7 @@ public List doInTransaction(TransactionStatus status) { publishDeletedVlanRanges(deletedVlanRangeToPublish); if (_networksDao.findById(network.getId()) == null) { // remove its related ACL permission - final Pair, Long> networkMsg = new Pair, Long>(Network.class, networkFinal.getId()); + final Pair, Long> networkMsg = new Pair<>(Network.class, networkFinal.getId()); _messageBus.publish(_name, EntityManager.MESSAGE_REMOVE_ENTITY_EVENT, PublishScope.LOCAL, networkMsg); } UsageEventUtils.publishNetworkDeletion(network); @@ -3517,6 +3612,23 @@ public List doInTransaction(TransactionStatus status) { return success; } + /** + * If it is a shared network with {@link ACLType#Domain}, it will belong to account {@link Account#ACCOUNT_ID_SYSTEM} and the resources will be not incremented for the + * domain. Therefore, we force the recalculation of the domain's resource count in this case. Otherwise, it will change the count for the account owner. + * @param incrementAccountResourceCount If true, the account resource count will be incremented by 1; otherwise, it will decremented by 1. + */ + private void changeAccountResourceCountOrRecalculateDomainResourceCount(Long accountId, Long domainId, boolean displayNetwork, boolean incrementAccountResourceCount) { + if (Account.ACCOUNT_ID_SYSTEM == accountId && ObjectUtils.isNotEmpty(domainId)) { + _resourceLimitMgr.recalculateDomainResourceCount(domainId, ResourceType.network, null); + } else { + if (incrementAccountResourceCount) { + _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.network, displayNetwork); + } else { + _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.network, displayNetwork); + } + } + } + private void publishDeletedVlanRanges(List deletedVlanRangeToPublish) { if (CollectionUtils.isNotEmpty(deletedVlanRangeToPublish)) { for (VlanVO vlan : deletedVlanRangeToPublish) { @@ -3526,10 +3638,8 @@ private void publishDeletedVlanRanges(List deletedVlanRangeToPublish) { } @Override - public boolean resourceCountNeedsUpdate(final NetworkOffering ntwkOff, final ACLType aclType) { - //Update resource count only for Isolated account specific non-system networks - final boolean updateResourceCount = ntwkOff.getGuestType() == GuestType.Isolated && !ntwkOff.isSystemOnly() && aclType == ACLType.Account; - return updateResourceCount; + public boolean isResourceCountUpdateNeeded(NetworkOffering networkOffering) { + return !networkOffering.isSystemOnly(); } protected Pair> deleteVlansInNetwork(final NetworkVO network, final long userId, final Account callerAccount) { @@ -3551,7 +3661,7 @@ protected Pair> deleteVlansInNetwork(final NetworkVO netwo //cleanup private vlans final int privateIpAllocCount = _privateIpDao.countAllocatedByNetworkId(networkId); if (privateIpAllocCount > 0) { - logger.warn("Can't delete Private ip range for network {} as it has allocated ip addresses", network); + logger.warn("Can't delete Private IP range for Network {} as it has allocated IP addresses", network); result = false; } else { _privateIpDao.deleteByNetworkId(networkId); @@ -3586,13 +3696,14 @@ protected void runInContext() { public void reallyRun() { try { - final List shutdownList = new ArrayList(); + final List shutdownList = new ArrayList<>(); final long currentTime = System.currentTimeMillis() / 1000; - final HashMap stillFree = new HashMap(); + final HashMap stillFree = new HashMap<>(); final List networkIds = _networksDao.findNetworksToGarbageCollect(); - final int netGcWait = NumbersUtil.parseInt(_configDao.getValue(NetworkGcWait.key()), 60); - logger.info("NetworkGarbageCollector uses '{}' seconds for GC interval.", netGcWait); + final int netGcWait = NetworkGcWait.value(); + final int netGcInterval = NetworkGcInterval.value(); + logger.info("NetworkGarbageCollector uses '{}' seconds for GC wait and '{}' seconds for GC interval.", netGcWait, netGcInterval); for (final Long networkId : networkIds) { if (!_networkModel.isNetworkReadyForGc(networkId)) { @@ -3700,7 +3811,7 @@ public boolean restartNetwork(final Long networkId, final Account callerAccount, try { VMInstanceVO instanceVO = _vmDao.findById(router.getId()); if (instanceVO == null) { - logger.info("Did not find a virtual router instance for the network"); + logger.info("Did not find a virtual router Instance for the Network"); continue; } Pair patched = mgr.updateSystemVM(instanceVO, true); @@ -3776,7 +3887,7 @@ public boolean areRoutersRunning(final List routers) { public void cleanupNicDhcpDnsEntry(Network network, VirtualMachineProfile vmProfile, NicProfile nicProfile) { final List networkProviders = getNetworkProviders(network.getId()); - for (final NetworkElement element : networkElements) { + for (final NetworkElement element : getNetworkElementsIncludingExtensions()) { if (networkProviders.contains(element.getProvider())) { if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) { throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: " @@ -3793,6 +3904,17 @@ public void cleanupNicDhcpDnsEntry(Network network, VirtualMachineProfile vmProf } } } + if (vmProfile.getType() == Type.User && element.getProvider() != null) { + if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Dns) + && _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dns, element.getProvider()) && element instanceof DnsServiceProvider) { + final DnsServiceProvider sp = (DnsServiceProvider) element; + try { + sp.removeDnsEntry(network, nicProfile, vmProfile); + } catch (ResourceUnavailableException e) { + logger.error("Failed to remove dns entry due to: ", e); + } + } + } } } } @@ -3812,7 +3934,7 @@ public void cleanupNicDhcpDnsEntry(Network network, VirtualMachineProfile vmProf * @throws InsufficientCapacityException */ private boolean rollingRestartRouters(final NetworkVO network, final NetworkOffering offering, final DeployDestination dest, final ReservationContext context) throws ResourceUnavailableException, ConcurrentOperationException, InsufficientCapacityException { - if (!NetworkOrchestrationService.RollingRestartEnabled.value()) { + if (!isRollingRestartSupport(network)) { if (shutdownNetworkElementsAndResources(context, true, network)) { implementNetworkElementsAndResources(dest, context, network, offering); return true; @@ -3860,6 +3982,20 @@ private boolean rollingRestartRouters(final NetworkVO network, final NetworkOffe return areRoutersRunning(routerDao.findByNetwork(network.getId())); } + private boolean isRollingRestartSupport(final NetworkVO network) { + if (!NetworkOrchestrator.RollingRestartEnabled.value()) { + return false; + } + List services = _ntwkSrvcDao.getServicesInNetwork(network.getId()); + for (NetworkServiceMapVO service : services) { + NetworkElement element = _networkModel.getElementImplementingProvider(service.getProvider()); + if (element == null || !element.rollingRestartSupported()) { + return false; + } + } + return true; + } + private void setRestartRequired(final NetworkVO network, final boolean restartRequired) { logger.debug("Marking network {} with restartRequired={}", network, restartRequired); network.setRestartRequired(restartRequired); @@ -3958,7 +4094,7 @@ protected boolean isSharedNetworkOfferingWithServices(final long networkOffering @Override public List listVmNics(final long vmId, final Long nicId, final Long networkId, String keyword) { - List result = null; + List result; if (keyword == null || keyword.isEmpty()) { if (nicId == null && networkId == null) { @@ -4001,8 +4137,8 @@ public boolean reallocate(final VirtualMachineProfile vm, final DataCenterDeploy if (dc.getNetworkType() == NetworkType.Basic) { final List nics = _nicDao.listByVmId(vmInstance.getId()); final NetworkVO network = _networksDao.findById(nics.get(0).getNetworkId()); - final LinkedHashMap> profiles = new LinkedHashMap>(); - profiles.put(network, new ArrayList()); + final LinkedHashMap> profiles = new LinkedHashMap<>(); + profiles.put(network, new ArrayList<>()); Transaction.execute(new TransactionCallbackWithExceptionNoReturn() { @Override @@ -4136,7 +4272,7 @@ private boolean shutdownNetworkResources(final Network network, final Account ca // Mark all static rules as revoked and apply them on the backend (not in the DB) final List firewallStaticNatRules = _firewallDao.listByNetworkAndPurpose(network.getId(), Purpose.StaticNat); - final List staticNatRules = new ArrayList(); + final List staticNatRules = new ArrayList<>(); logger.debug("Releasing {} static nat rules for network {} as a part of shutdownNetworkRules", firewallStaticNatRules.size(), network); for (final FirewallRuleVO firewallStaticNatRule : firewallStaticNatRules) { @@ -4258,7 +4394,7 @@ private boolean shutdownNetworkResources(final Network network, final Account ca // Get all ip addresses, mark as releasing and release them on the backend final List userIps = _ipAddressDao.listByAssociatedNetwork(network.getId(), null); - final List publicIpsToRelease = new ArrayList(); + final List publicIpsToRelease = new ArrayList<>(); if (userIps != null && !userIps.isEmpty()) { for (final IPAddressVO userIp : userIps) { userIp.setState(IpAddress.State.Releasing); @@ -4307,7 +4443,7 @@ public void processConnect(final Host host, final StartupCommand cmd, final bool final String dataCenter = startup.getDataCenter(); - long dcId = -1; + long dcId; DataCenterVO dc = _dcDao.findByName(dataCenter); if (dc == null) { try { @@ -4324,7 +4460,7 @@ public void processConnect(final Host host, final StartupCommand cmd, final bool logger.debug("Host's hypervisorType is: {}", hypervisorType); - final List networkInfoList = new ArrayList(); + final List networkInfoList = new ArrayList<>(); // list all physicalnetworks in the zone & for each get the network names final List physicalNtwkList = _physicalNetworkDao.listByZone(dcId); @@ -4403,8 +4539,8 @@ public boolean processTimeout(final long agentId, final long seq) { @Override public Map finalizeServicesAndProvidersForNetwork(final NetworkOffering offering, final Long physicalNetworkId) { - final Map svcProviders = new HashMap(); - final Map> providerSvcs = new HashMap>(); + final Map svcProviders = new HashMap<>(); + final Map> providerSvcs = new HashMap<>(); final List servicesMap = _ntwkOfferingSrvcDao.listByNetworkOfferingId(offering.getId()); final boolean checkPhysicalNetwork = physicalNetworkId != null ? true : false; @@ -4421,6 +4557,12 @@ public Map finalizeServicesAndProvidersForNetwork(final NetworkO if (provider == null) { provider = _networkModel.getDefaultUniqueProviderForService(service).getName(); + } else { + final Provider resolvedProvider = _networkModel.resolveProvider(provider); + if (resolvedProvider == null) { + throw new InvalidParameterValueException("Invalid provider " + provider + " configured for service " + service); + } + provider = resolvedProvider.getName(); } // check that provider is supported @@ -4434,7 +4576,7 @@ public Map finalizeServicesAndProvidersForNetwork(final NetworkO svcProviders.put(service, provider); List l = providerSvcs.get(provider); if (l == null) { - providerSvcs.put(provider, l = new ArrayList()); + providerSvcs.put(provider, l = new ArrayList<>()); } l.add(service); } @@ -4444,9 +4586,12 @@ public Map finalizeServicesAndProvidersForNetwork(final NetworkO private List getNetworkProviders(final long networkId) { final List providerNames = _ntwkSrvcDao.getDistinctProviders(networkId); - final List providers = new ArrayList(); + final List providers = new ArrayList<>(); for (final String providerName : providerNames) { - providers.add(Network.Provider.getProvider(providerName)); + final Provider provider = _networkModel.resolveProvider(providerName); + if (provider != null) { + providers.add(provider); + } } return providers; @@ -4501,7 +4646,7 @@ public NicProfile createNicForVm(final Network network, final NicProfile request nic = allocateNic(requested, network, isDefaultNic, deviceId, vmProfile).first(); if (nic == null) { - throw new CloudRuntimeException("Failed to allocate nic for vm " + vm + " in network " + network); + throw new CloudRuntimeException("Failed to allocate nic for Instance " + vm + " in network " + network); } //Update vm_network_map table @@ -4509,18 +4654,18 @@ public NicProfile createNicForVm(final Network network, final NicProfile request final VMNetworkMapVO vno = new VMNetworkMapVO(vm.getId(), network.getId()); _vmNetworkMapDao.persist(vno); } - logger.debug("Nic is allocated successfully for vm {} in network {}", vm, network); + logger.debug("NIC is allocated successfully for Instance {} in Network {}", vm, network); } //2) prepare nic if (prepare) { final Pair implemented = implementNetwork(nic.getNetworkId(), dest, context, vmProfile.getVirtualMachine().getType() == Type.DomainRouter); if (implemented == null || implemented.first() == null) { - logger.warn("Failed to implement network {} as a part of preparing nic {}", network, nic); - throw new CloudRuntimeException(String.format("Failed to implement network %s as a part preparing nic %s", network, nic)); + logger.warn("Failed to implement Network {} as a part of preparing NIC {}", network, nic); + throw new CloudRuntimeException(String.format("Failed to implement Network %s as a part preparing NIC %s", network, nic)); } nic = prepareNic(vmProfile, dest, context, nic.getId(), implemented.second()); - logger.debug("Nic is prepared successfully for vm {} in network {}", vm, network); + logger.debug("NIC is prepared successfully for Instance {} in Network {}", vm, network); } return nic; @@ -4539,7 +4684,7 @@ private boolean getNicProfileDefaultNic(NicProfile nicProfile) { @Override public List getNicProfiles(final Long vmId, HypervisorType hypervisorType) { final List nics = _nicDao.listByVmId(vmId); - final List profiles = new ArrayList(); + final List profiles = new ArrayList<>(); if (nics != null) { for (final Nic nic : nics) { @@ -4605,14 +4750,14 @@ private void setStateMachine() { } private Map> getServiceProvidersMap(final long networkId) { - final Map> map = new HashMap>(); + final Map> map = new HashMap<>(); final List nsms = _ntwkSrvcDao.getServicesInNetwork(networkId); for (final NetworkServiceMapVO nsm : nsms) { Set providers = map.get(Service.getService(nsm.getService())); if (providers == null) { - providers = new HashSet(); + providers = new HashSet<>(); } - providers.add(Provider.getProvider(nsm.getProvider())); + providers.add(_networkModel.resolveProvider(nsm.getProvider())); map.put(Service.getService(nsm.getService()), providers); } return map; @@ -4622,14 +4767,14 @@ private Map> getServiceProvidersMap(final long networkId) public List getProvidersForServiceInNetwork(final Network network, final Service service) { final Map> service2ProviderMap = getServiceProvidersMap(network.getId()); if (service2ProviderMap.get(service) != null) { - final List providers = new ArrayList(service2ProviderMap.get(service)); + final List providers = new ArrayList<>(service2ProviderMap.get(service)); return providers; } return null; } protected List getElementForServiceInNetwork(final Network network, final Service service) { - final List elements = new ArrayList(); + final List elements = new ArrayList<>(); final List providers = getProvidersForServiceInNetwork(network, service); //Only support one provider now if (providers == null) { @@ -4663,7 +4808,7 @@ public LoadBalancingServiceProvider getLoadBalancingProviderForNetwork(final Net final List lbElements = getElementForServiceInNetwork(network, Service.Lb); NetworkElement lbElement = null; if (lbElements.size() > 1) { - String providerName = null; + String providerName; //get network offering details final NetworkOffering off = _entityMgr.findById(NetworkOffering.class, network.getNetworkOfferingId()); if (lbScheme == Scheme.Public) { @@ -4738,7 +4883,7 @@ public NicVO savePlaceholderNic(final Network network, final String ip4Address, @Override public Pair importNic(final String macAddress, int deviceId, final Network network, final Boolean isDefaultNic, final VirtualMachine vm, final Network.IpAddresses ipAddresses, final DataCenter dataCenter, final boolean forced) throws ConcurrentOperationException, InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException { - logger.debug("Allocating nic for vm {} in network {} during import", vm, network); + logger.debug("Allocating NIC for Instance {} in Network {} during import", vm, network); String selectedIp = null; if (ipAddresses != null && StringUtils.isNotEmpty(ipAddresses.getIp4Address())) { if (ipAddresses.getIp4Address().equals("auto")) { @@ -4751,7 +4896,7 @@ public Pair importNic(final String macAddress, int deviceId } } final String finalSelectedIp = selectedIp; - final NicVO vo = Transaction.execute(new TransactionCallback() { + final NicVO vo = Transaction.execute(new TransactionCallback<>() { @Override public NicVO doInTransaction(TransactionStatus status) { if (StringUtils.isBlank(macAddress)) { @@ -4804,6 +4949,18 @@ public NicVO doInTransaction(TransactionStatus status) { } }); + if (selectedIp != null && GuestType.Shared.equals(network.getGuestType())) { + IPAddressVO ipAddressVO = _ipAddressDao.findByIpAndSourceNetworkId(network.getId(), selectedIp); + if (ipAddressVO != null && IpAddress.State.Free.equals(ipAddressVO.getState())) { + ipAddressVO.setState(IPAddressVO.State.Allocated); + ipAddressVO.setAllocatedTime(new Date()); + Account account = _accountDao.findById(vm.getAccountId()); + ipAddressVO.setAllocatedInDomainId(account.getDomainId()); + ipAddressVO.setAllocatedToAccountId(account.getId()); + _ipAddressDao.update(ipAddressVO.getId(), ipAddressVO); + } + } + final Integer networkRate = _networkModel.getNetworkRate(network.getId(), vm.getId()); final NicProfile vmNic = new NicProfile(vo, network, vo.getBroadcastUri(), vo.getIsolationUri(), networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vm.getHypervisorType(), network)); @@ -4815,15 +4972,15 @@ protected String getSelectedIpForNicImport(Network network, DataCenter dataCente if (network.getGuestType() == GuestType.L2) { return null; } - return dataCenter.getNetworkType() == NetworkType.Basic ? - getSelectedIpForNicImportOnBasicZone(ipAddresses.getIp4Address(), network, dataCenter): + return GuestType.Shared.equals(network.getGuestType()) ? + getSelectedIpForNicImportOnSharedNetwork(ipAddresses.getIp4Address(), network, dataCenter): _ipAddrMgr.acquireGuestIpAddress(network, ipAddresses.getIp4Address()); } - protected String getSelectedIpForNicImportOnBasicZone(String requestedIp, Network network, DataCenter dataCenter) { + protected String getSelectedIpForNicImportOnSharedNetwork(String requestedIp, Network network, DataCenter dataCenter) { IPAddressVO ipAddressVO = StringUtils.isBlank(requestedIp) ? _ipAddressDao.findBySourceNetworkIdAndDatacenterIdAndState(network.getId(), dataCenter.getId(), IpAddress.State.Free): - _ipAddressDao.findByIp(requestedIp); + _ipAddressDao.findByIpAndSourceNetworkId(network.getId(), requestedIp); if (ipAddressVO == null || ipAddressVO.getState() != IpAddress.State.Free) { String msg = String.format("Cannot find a free IP to assign to VM NIC on network %s", network.getName()); logger.error(msg); @@ -4885,10 +5042,10 @@ public void unmanageNics(VirtualMachineProfile vm) { @Override public void expungeLbVmRefs(List vmIds, Long batchSize) { - if (CollectionUtils.isEmpty(networkElements) || CollectionUtils.isEmpty(vmIds)) { + if (CollectionUtils.isEmpty(vmIds)) { return; } - for (NetworkElement element : networkElements) { + for (NetworkElement element : getNetworkElementsIncludingExtensions()) { if (element instanceof LoadBalancingServiceProvider) { LoadBalancingServiceProvider lbProvider = (LoadBalancingServiceProvider)element; lbProvider.expungeLbVmRefs(vmIds, batchSize); @@ -4902,15 +5059,16 @@ public String getConfigComponentName() { } public static final ConfigKey NetworkGcWait = new ConfigKey(Integer.class, "network.gc.wait", "Advanced", "600", - "Time (in seconds) to wait before shutting down a network that's not in used", false, Scope.Global, null); + "Time (in seconds) to wait before shutting down a network that's not in used", true, Scope.Global, null); public static final ConfigKey NetworkGcInterval = new ConfigKey(Integer.class, "network.gc.interval", "Advanced", "600", - "Seconds to wait before checking for networks to shutdown", true, Scope.Global, null); + "Seconds to wait before checking for networks to shutdown", false, Scope.Global, null); @Override public ConfigKey[] getConfigKeys() { return new ConfigKey[]{NetworkGcWait, NetworkGcInterval, NetworkLockTimeout, DeniedRoutes, - GuestDomainSuffix, NetworkThrottlingRate, MinVRVersion, + GuestDomainSuffix, NetworkThrottlingRate, VmNetworkThrottlingRate, MinVRVersion, DhcpLeaseTimeout, PromiscuousMode, MacAddressChanges, ForgedTransmits, MacLearning, RollingRestartEnabled, - TUNGSTEN_ENABLED, NSX_ENABLED, NETRIS_ENABLED }; + TUNGSTEN_ENABLED, NSX_ENABLED, NETRIS_ENABLED, NETWORK_LB_HAPROXY_MAX_CONN, + NETWORK_LB_HAPROXY_IDLE_TIMEOUT}; } } diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/StorageOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/StorageOrchestrator.java index 0773c20b6b98..403e83bccf99 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/StorageOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/StorageOrchestrator.java @@ -25,9 +25,13 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -36,7 +40,22 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.MigrateBackupsBetweenSecondaryStoragesCommand; +import com.cloud.agent.api.MigrateBetweenSecondaryStoragesCommandAnswer; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.exception.AgentUnavailableException; +import com.cloud.exception.OperationTimedoutException; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.template.TemplateManager; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; import org.apache.cloudstack.api.response.MigrationResponse; +import org.apache.cloudstack.backup.BackupDetailVO; +import org.apache.cloudstack.backup.dao.BackupDetailsDao; import org.apache.cloudstack.engine.orchestration.service.StorageOrchestrationService; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; @@ -45,12 +64,16 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SecondaryStorageService.DataObjectResult; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService; +import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService.TemplateApiResult; import org.apache.cloudstack.framework.async.AsyncCallFuture; 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.storage.ImageStoreService.MigrationPolicy; +import org.apache.cloudstack.storage.backup.BackupObject; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; @@ -71,9 +94,12 @@ import com.cloud.utils.Pair; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.logging.log4j.ThreadContext; public class StorageOrchestrator extends ManagerBase implements StorageOrchestrationService, Configurable { + private static final String LOGCONTEXTID = "logcontextid"; + @Inject SnapshotDataStoreDao snapshotDataStoreDao; @Inject @@ -91,11 +117,28 @@ public class StorageOrchestrator extends ManagerBase implements StorageOrchestra @Inject private SecondaryStorageService secStgSrv; @Inject + TemplateService templateService; + @Inject TemplateDataStoreDao templateDataStoreDao; @Inject VolumeDataStoreDao volumeDataStoreDao; @Inject DataMigrationUtility migrationHelper; + @Inject + TemplateManager templateManager; + @Inject + VMTemplateDao templateDao; + @Inject + TemplateDataFactory templateDataFactory; + @Inject + DataCenterDao dcDao; + @Inject + AgentManager agentManager; + @Inject + HostDao hostDao; + @Inject + BackupDetailsDao backupDetailDao; + ConfigKey ImageStoreImbalanceThreshold = new ConfigKey<>("Advanced", Double.class, "image.store.imbalance.threshold", @@ -106,6 +149,10 @@ public class StorageOrchestrator extends ManagerBase implements StorageOrchestra Integer numConcurrentCopyTasksPerSSVM = 2; + private final Map zoneExecutorMap = new HashMap<>(); + private final Map zonePendingWorkCountMap = new HashMap<>(); + private final Map zoneKvmIncrementalExecutorMap = new ConcurrentHashMap<>(); + @Override public String getConfigComponentName() { return StorageOrchestrationService.class.getName(); @@ -148,7 +195,9 @@ public MigrationResponse migrateData(Long srcDataStoreId, List destDatasto DataStore srcDatastore = dataStoreManager.getDataStore(srcDataStoreId, DataStoreRole.Image); Map, Long>> snapshotChains = new HashMap<>(); Map, Long>> childTemplates = new HashMap<>(); - files = migrationHelper.getSortedValidSourcesList(srcDatastore, snapshotChains, childTemplates); + Map>, Long>> backupChains = new HashMap<>(); + files = migrationHelper.getSortedValidSourcesList(srcDatastore, snapshotChains, childTemplates, backupChains); + if (files.isEmpty()) { return new MigrationResponse(String.format("No files in Image store: %s to migrate", srcDatastore), migrationPolicy.toString(), true); @@ -167,8 +216,6 @@ public MigrationResponse migrateData(Long srcDataStoreId, List destDatasto double meanstddev = getStandardDeviation(storageCapacities); double threshold = ImageStoreImbalanceThreshold.value(); MigrationResponse response = null; - ThreadPoolExecutor executor = new ThreadPoolExecutor(numConcurrentCopyTasksPerSSVM , numConcurrentCopyTasksPerSSVM, 30, - TimeUnit.MINUTES, new MigrateBlockingQueue<>(numConcurrentCopyTasksPerSSVM)); Date start = new Date(); if (meanstddev < threshold && migrationPolicy == MigrationPolicy.BALANCE) { logger.debug("mean std deviation of the image stores is below threshold, no migration required"); @@ -177,7 +224,7 @@ public MigrationResponse migrateData(Long srcDataStoreId, List destDatasto } int skipped = 0; - List>> futures = new ArrayList<>(); + List> futures = new ArrayList<>(); while (true) { DataObject chosenFileForMigration = null; if (files.size() > 0) { @@ -206,7 +253,7 @@ public MigrationResponse migrateData(Long srcDataStoreId, List destDatasto } if (shouldMigrate(chosenFileForMigration, srcDatastore.getId(), destDatastoreId, storageCapacities, snapshotChains, childTemplates, migrationPolicy)) { - storageCapacities = migrateAway(chosenFileForMigration, storageCapacities, snapshotChains, childTemplates, srcDatastore, destDatastoreId, executor, futures); + storageCapacities = migrateAway(chosenFileForMigration, storageCapacities, snapshotChains, childTemplates, backupChains, srcDatastore, destDatastoreId, futures); } else { if (migrationPolicy == MigrationPolicy.BALANCE) { continue; @@ -217,7 +264,7 @@ public MigrationResponse migrateData(Long srcDataStoreId, List destDatasto } } Date end = new Date(); - handleSnapshotMigration(srcDataStoreId, start, end, migrationPolicy, futures, storageCapacities, executor); + handleSnapshotMigration(srcDataStoreId, start, end, migrationPolicy, futures, storageCapacities); return handleResponse(futures, migrationPolicy, message, success); } @@ -235,7 +282,7 @@ public MigrationResponse migrateResources(Long srcImgStoreId, Long destImgStoreI List templates = templateDataStoreDao.listByStoreIdAndTemplateIds(srcImgStoreId, templateIdList); List snapshots = snapshotDataStoreDao.listByStoreAndSnapshotIds(srcImgStoreId, DataStoreRole.Image, snapshotIdList); - if (!migrationHelper.filesReadyToMigrate(srcImgStoreId, templates, snapshots, Collections.emptyList())) { + if (!migrationHelper.filesReadyToMigrate(srcImgStoreId, templates, snapshots, Collections.emptyList(), Collections.emptyList())) { throw new CloudRuntimeException("Migration failed as there are data objects which are not Ready - i.e, they may be in Migrating, creating, copying, etc. states"); } files = migrationHelper.getSortedValidSourcesList(srcDatastore, snapshotChains, childTemplates, templates, snapshots); @@ -250,9 +297,7 @@ public MigrationResponse migrateResources(Long srcImgStoreId, Long destImgStoreI storageCapacities = getStorageCapacities(storageCapacities, srcImgStoreId); storageCapacities = getStorageCapacities(storageCapacities, destImgStoreId); - ThreadPoolExecutor executor = new ThreadPoolExecutor(numConcurrentCopyTasksPerSSVM, numConcurrentCopyTasksPerSSVM, 30, - TimeUnit.MINUTES, new MigrateBlockingQueue<>(numConcurrentCopyTasksPerSSVM)); - List>> futures = new ArrayList<>(); + List> futures = new ArrayList<>(); Date start = new Date(); while (true) { @@ -272,7 +317,7 @@ public MigrationResponse migrateResources(Long srcImgStoreId, Long destImgStoreI } if (storageCapacityBelowThreshold(storageCapacities, destImgStoreId)) { - storageCapacities = migrateAway(chosenFileForMigration, storageCapacities, snapshotChains, childTemplates, srcDatastore, destImgStoreId, executor, futures); + storageCapacities = migrateAway(chosenFileForMigration, storageCapacities, snapshotChains, childTemplates, null, srcDatastore, destImgStoreId, futures); } else { message = "Migration failed. Destination store doesn't have enough capacity for migration"; success = false; @@ -289,7 +334,7 @@ public MigrationResponse migrateResources(Long srcImgStoreId, Long destImgStoreI SnapshotInfo snapshotInfo = snapshotFactory.getSnapshot(snap.getSnapshotId(), snap.getDataStoreId(), DataStoreRole.Image); SnapshotInfo parentSnapshot = snapshotInfo.getParent(); if (snapshotInfo.getDataStore().getId() == srcImgStoreId && parentSnapshot != null && migratedSnapshotIdList.contains(parentSnapshot.getSnapshotId())) { - futures.add(executor.submit(new MigrateDataTask(snapshotInfo, srcDatastore, dataStoreManager.getDataStore(destImgStoreId, DataStoreRole.Image)))); + futures.add(submit(srcDatastore.getScope().getScopeId(), new MigrateDataTask(snapshotInfo, srcDatastore, dataStoreManager.getDataStore(destImgStoreId, DataStoreRole.Image)))); } }); } @@ -297,6 +342,12 @@ public MigrationResponse migrateResources(Long srcImgStoreId, Long destImgStoreI return handleResponse(futures, null, message, success); } + @Override + public Future orchestrateTemplateCopyFromSecondaryStores(long srcTemplateId, DataStore destStore) { + Long dstZoneId = destStore.getScope().getScopeId(); + return submit(dstZoneId, new CopyTemplateFromSecondaryStorageTask(srcTemplateId, destStore)); + } + protected Pair migrateCompleted(Long destDatastoreId, DataStore srcDatastore, List files, MigrationPolicy migrationPolicy, int skipped) { String message = ""; boolean success = true; @@ -330,42 +381,158 @@ protected Map> migrateAway( Map, Long>> snapshotChains, Map, Long>> templateChains, + Map>, Long>> backupChains, DataStore srcDatastore, Long destDatastoreId, - ThreadPoolExecutor executor, - List>> futures) { + List> futures) { Long fileSize = migrationHelper.getFileSize(chosenFileForMigration, snapshotChains, templateChains); - storageCapacities = assumeMigrate(storageCapacities, srcDatastore.getId(), destDatastoreId, fileSize); - long activeSsvms = migrationHelper.activeSSVMCount(srcDatastore); - long totalJobs = activeSsvms * numConcurrentCopyTasksPerSSVM; - // Increase thread pool size with increase in number of SSVMs - if ( totalJobs > executor.getCorePoolSize()) { - executor.setMaximumPoolSize((int) (totalJobs)); - executor.setCorePoolSize((int) (totalJobs)); + DataStore destDataStore = dataStoreManager.getDataStore(destDatastoreId, DataStoreRole.Image); + + boolean isKvmIncrementalBackup = backupChains != null && chosenFileForMigration instanceof BackupObject && backupChains.containsKey(chosenFileForMigration); + + if (isKvmIncrementalBackup) { + MigrateKvmIncrementalBackupTask task = new MigrateKvmIncrementalBackupTask(chosenFileForMigration, backupChains, srcDatastore, destDataStore); + futures.add(submitKvmIncrementalMigration(srcDatastore.getScope().getScopeId(), task)); + logger.debug("Incremental backup migration {} submitted to incremental pool.", chosenFileForMigration.getUuid()); + } else { + createMigrateDataTask(chosenFileForMigration, snapshotChains, templateChains, srcDatastore, destDataStore, futures); + } + + return storageCapacities; + } + + private void migrateKvmIncrementalBackupChain(DataObject chosenFileForMigration, Map>, Long>> backupChains, DataStore srcDatastore, DataStore destDataStore) { + Transaction.execute((TransactionCallback) status -> { + MigrateBetweenSecondaryStoragesCommandAnswer answer = null; + + try { + List> backupChain = backupChains.get(chosenFileForMigration).first(); + MigrateBackupsBetweenSecondaryStoragesCommand migrateBetweenSecondaryStoragesCmd = new MigrateBackupsBetweenSecondaryStoragesCommand(backupChain.stream().map(list -> list.stream().map(BackupObject::getTO).collect(Collectors.toList())) + .collect(Collectors.toList()), srcDatastore.getTO(), destDataStore.getTO()); + + HostVO host = getAvailableHost(((BackupObject) chosenFileForMigration).getZoneId()); + if (host == null) { + throw new CloudRuntimeException("No hosts found to send migrate command."); + } + + migrateBetweenSecondaryStoragesCmd.setWait(StorageManager.AgentMaxDataMigrationWaitTime.valueIn(host.getClusterId())); + answer = (MigrateBetweenSecondaryStoragesCommandAnswer) agentManager.send(host.getId(), migrateBetweenSecondaryStoragesCmd); + if (answer == null || !answer.getResult()) { + logger.warn("Unable to migrate backups [{}].", backupChain); + throw new CloudRuntimeException("Unable to migrate KVM incremental backups to another secondary storage"); + } + + } catch (final OperationTimedoutException | AgentUnavailableException e) { + throw new CloudRuntimeException("Error while migrating KVM incremental backup chain. Check the logs for more information.", e); + } finally { + if (answer != null) { + updateBackupsReference(destDataStore, answer); + } + } + return answer.getResult(); + }); + } + + private void updateBackupsReference(DataStore destDataStore, MigrateBetweenSecondaryStoragesCommandAnswer answer) { + for (Pair backupIdAndUpdatedCheckpointPath : answer.getMigratedResources()) { + Long backupId = backupIdAndUpdatedCheckpointPath.first(); + BackupDetailVO backupDetail = backupDetailDao.findDetail(backupId, BackupDetailsDao.IMAGE_STORE_ID); + String destDataStoreId = String.valueOf(destDataStore.getId()); + + if (backupDetail == null) { + logger.warn("No details found for backup [{}]. Creating new entry with image store ID [{}].", backupId, destDataStoreId); + backupDetailDao.addDetail(backupId, BackupDetailsDao.IMAGE_STORE_ID, destDataStoreId, false); + continue; + } + + backupDetail.setValue(destDataStoreId); + backupDetailDao.update(backupDetail.getId(), backupDetail); } + } - MigrateDataTask task = new MigrateDataTask(chosenFileForMigration, srcDatastore, dataStoreManager.getDataStore(destDatastoreId, DataStoreRole.Image)); - if (chosenFileForMigration instanceof SnapshotInfo ) { + private HostVO getAvailableHost(long zoneId) throws AgentUnavailableException, OperationTimedoutException { + List hosts = hostDao.listByDataCenterIdAndHypervisorType(zoneId, Hypervisor.HypervisorType.KVM); + if (CollectionUtils.isNotEmpty(hosts)) { + return hosts.get(new Random().nextInt(hosts.size())); + } + + return null; + } + + private void createMigrateDataTask(DataObject chosenFileForMigration, Map, Long>> snapshotChains, Map, Long>> templateChains, DataStore srcDatastore, DataStore destDataStore, List> futures) { + MigrateDataTask task = new MigrateDataTask(chosenFileForMigration, srcDatastore, destDataStore); + if (chosenFileForMigration instanceof SnapshotInfo) { task.setSnapshotChains(snapshotChains); } if (chosenFileForMigration instanceof TemplateInfo) { task.setTemplateChain(templateChains); } - futures.add((executor.submit(task))); + futures.add(submit(srcDatastore.getScope().getScopeId(), task)); logger.debug("Migration of {}: {} is initiated.", chosenFileForMigration.getType().name(), chosenFileForMigration.getUuid()); - return storageCapacities; } + protected Future submit(Long zoneId, Callable task) { + ThreadPoolExecutor executor; + synchronized (this) { + if (!zoneExecutorMap.containsKey(zoneId)) { + zoneExecutorMap.put(zoneId, new ThreadPoolExecutor(numConcurrentCopyTasksPerSSVM, numConcurrentCopyTasksPerSSVM, + 30, TimeUnit.MINUTES, new MigrateBlockingQueue<>(numConcurrentCopyTasksPerSSVM))); + zonePendingWorkCountMap.put(zoneId, 0); + } + zonePendingWorkCountMap.merge(zoneId, 1, Integer::sum); + scaleExecutorIfNecessary(zoneId); + executor = zoneExecutorMap.get(zoneId); + } + return executor.submit(task); + + } + protected synchronized Future submitKvmIncrementalMigration(Long zoneId, Callable task) { + if (!zoneKvmIncrementalExecutorMap.containsKey(zoneId)) { + zoneKvmIncrementalExecutorMap.put(zoneId, Executors.newSingleThreadExecutor()); + } + return zoneKvmIncrementalExecutorMap.get(zoneId).submit(task); + } - private MigrationResponse handleResponse(List>> futures, MigrationPolicy migrationPolicy, String message, boolean success) { + protected void scaleExecutorIfNecessary(Long zoneId) { + long activeSsvms = migrationHelper.activeSSVMCount(zoneId); + long totalJobs = activeSsvms * numConcurrentCopyTasksPerSSVM; + ThreadPoolExecutor executor = zoneExecutorMap.get(zoneId); + if (totalJobs > executor.getCorePoolSize()) { + logger.debug("Scaling up executor of zone [{}] from [{}] to [{}] threads.", zoneId, executor.getCorePoolSize(), + totalJobs); + executor.setMaximumPoolSize((int) (totalJobs)); + executor.setCorePoolSize((int) (totalJobs)); + } + } + + protected synchronized void tryCleaningUpExecutor(Long zoneId) { + if (!zoneExecutorMap.containsKey(zoneId)) { + logger.debug("No executor exists for zone [{}].", zoneId); + return; + } + + zonePendingWorkCountMap.merge(zoneId, -1, Integer::sum); + Integer pendingWorkCount = zonePendingWorkCountMap.get(zoneId); + if (pendingWorkCount > 0) { + logger.debug("Not cleaning executor of zone [{}] yet, as there is [{}] pending work.", zoneId, pendingWorkCount); + return; + } + + logger.debug("Cleaning executor of zone [{}].", zoneId); + ThreadPoolExecutor executor = zoneExecutorMap.get(zoneId); + zoneExecutorMap.remove(zoneId); + executor.shutdown(); + } + + private MigrationResponse handleResponse(List> futures, MigrationPolicy migrationPolicy, String message, boolean success) { int successCount = 0; - for (Future> future : futures) { + for (Future future : futures) { try { - AsyncCallFuture res = future.get(); - if (res.get().isSuccess()) { + DataObjectResult res = future.get(); + if (res.isSuccess()) { successCount++; } } catch ( InterruptedException | ExecutionException e) { @@ -379,7 +546,7 @@ private MigrationResponse handleResponse(List>> futures, Map> storageCapacities, ThreadPoolExecutor executor) { + List> futures, Map> storageCapacities) { DataStore srcDatastore = dataStoreManager.getDataStore(srcDataStoreId, DataStoreRole.Image); List snaps = snapshotDataStoreDao.findSnapshots(srcDataStoreId, start, end); if (!snaps.isEmpty()) { @@ -395,12 +562,12 @@ private void handleSnapshotMigration(Long srcDataStoreId, Date start, Date end, storeId = dstores.get(1); } DataStore datastore = dataStoreManager.getDataStore(storeId, DataStoreRole.Image); - futures.add(executor.submit(new MigrateDataTask(snapshotInfo, srcDatastore, datastore))); + futures.add(submit(srcDatastore.getScope().getScopeId(), new MigrateDataTask(snapshotInfo, srcDatastore, datastore))); } if (parentSnapshot != null) { DataStore parentDS = dataStoreManager.getDataStore(parentSnapshot.getDataStore().getId(), DataStoreRole.Image); if (parentDS.getId() != snapshotInfo.getDataStore().getId()) { - futures.add(executor.submit(new MigrateDataTask(snapshotInfo, srcDatastore, parentDS))); + futures.add(submit(srcDatastore.getScope().getScopeId(), new MigrateDataTask(snapshotInfo, srcDatastore, parentDS))); } } } @@ -527,16 +694,19 @@ private double calculateStorageStandardDeviation(double[] metricValues, double m return standardDeviation.evaluate(metricValues, mean); } - private class MigrateDataTask implements Callable> { + private class MigrateDataTask implements Callable { private DataObject file; private DataStore srcDataStore; private DataStore destDataStore; private Map, Long>> snapshotChain; private Map, Long>> templateChain; + private String logid; + public MigrateDataTask(DataObject file, DataStore srcDataStore, DataStore destDataStore) { this.file = file; this.srcDataStore = srcDataStore; this.destDataStore = destDataStore; + this.logid = ThreadContext.get(LOGCONTEXTID); } public void setSnapshotChains(Map, Long>> snapshotChain) { @@ -557,8 +727,77 @@ public DataObject getFile() { } @Override - public AsyncCallFuture call() throws Exception { - return secStgSrv.migrateData(file, srcDataStore, destDataStore, snapshotChain, templateChain); + public DataObjectResult call() { + ThreadContext.put(LOGCONTEXTID, logid); + DataObjectResult result; + AsyncCallFuture future = secStgSrv.migrateData(file, srcDataStore, destDataStore, snapshotChain, templateChain); + try { + result = future.get(); + } catch (ExecutionException | InterruptedException e) { + logger.warn("Exception while migrating data to another secondary storage: {}", e.toString()); + result = new DataObjectResult(file); + result.setResult(e.toString()); + } + tryCleaningUpExecutor(srcDataStore.getScope().getScopeId()); + ThreadContext.clearAll(); + return result; } } + + private class CopyTemplateFromSecondaryStorageTask implements Callable { + private final long srcTemplateId; + private final DataStore destStore; + private final String logid; + + CopyTemplateFromSecondaryStorageTask(long srcTemplateId, DataStore destStore) { + this.srcTemplateId = srcTemplateId; + this.destStore = destStore; + this.logid = ThreadContext.get(LOGCONTEXTID); + } + + @Override + public TemplateApiResult call() { + ThreadContext.put(LOGCONTEXTID, logid); + TemplateApiResult result; + long destZoneId = destStore.getScope().getScopeId(); + TemplateInfo sourceTmpl = templateDataFactory.getTemplate(srcTemplateId, DataStoreRole.Image); + try { + templateService.handleTemplateCopyFromSecondaryStores(srcTemplateId, destStore); + result = new TemplateApiResult(sourceTmpl); + } finally { + tryCleaningUpExecutor(destZoneId); + ThreadContext.clearAll(); + } + + return result; + } + } + + private class MigrateKvmIncrementalBackupTask implements Callable { + private final DataObject chosenFile; + private final Map>, Long>> backupChains; + private final DataStore srcDataStore; + private final DataStore destDataStore; + + public MigrateKvmIncrementalBackupTask(DataObject chosenFile, Map>, Long>> backupChains, DataStore srcDataStore, DataStore destDataStore) { + this.chosenFile = chosenFile; + this.backupChains = backupChains; + this.srcDataStore = srcDataStore; + this.destDataStore = destDataStore; + } + + @Override + public DataObjectResult call() { + try { + migrateKvmIncrementalBackupChain(chosenFile, backupChains, srcDataStore, destDataStore); + return new DataObjectResult(chosenFile); + } catch (Exception e) { + logger.warn("Failed migrating incremental backup {} due to {}.", chosenFile.getUuid(), e); + DataObjectResult result = new DataObjectResult(chosenFile); + result.setResult(e.toString()); + return result; + } + } + } + } 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 ba50d5ff9fac..f4198819dd16 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,8 +38,11 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.agent.AgentManager; import com.cloud.deploy.DeploymentClusterPlanner; import com.cloud.exception.ResourceAllocationException; +import com.cloud.storage.clvm.ClvmPoolManager; +import com.cloud.resourcelimit.ReservationHelper; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.dao.VMTemplateDao; @@ -50,6 +53,7 @@ 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.InternalBackupService; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo; @@ -82,9 +86,17 @@ import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO; import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao; +import org.apache.cloudstack.resourcelimit.Reserver; import org.apache.cloudstack.secret.PassphraseVO; import org.apache.cloudstack.secret.dao.PassphraseDao; import org.apache.cloudstack.snapshot.SnapshotHelper; +import org.apache.cloudstack.kms.KMSManager; +import org.apache.cloudstack.kms.KMSKeyVO; +import org.apache.cloudstack.kms.KMSWrappedKeyVO; +import org.apache.cloudstack.kms.dao.KMSKeyDao; +import org.apache.cloudstack.kms.dao.KMSWrappedKeyDao; +import org.apache.cloudstack.framework.kms.KMSException; +import org.apache.cloudstack.framework.kms.WrappedKey; import org.apache.cloudstack.storage.command.CommandResult; import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; @@ -184,6 +196,7 @@ import com.cloud.vm.dao.UserVmCloneSettingDao; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDetailsDao; +import com.cloud.vm.dao.VMInstanceDao; public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrationService, Configurable { @@ -270,12 +283,27 @@ public enum UserVmCloneType { ConfigDepot configDepot; @Inject ConfigurationDao configurationDao; + @Inject + VMInstanceDao vmInstanceDao; + @Inject + ClvmPoolManager clvmPoolManager; + @Inject + AgentManager _agentMgr; @Inject protected SnapshotHelper snapshotHelper; @Inject private DataStoreProviderManager dataStoreProviderMgr; + @Inject + private KMSManager kmsManager; + @Inject + private KMSKeyDao kmsKeyDao; + @Inject + private KMSWrappedKeyDao kmsWrappedKeyDao; + + @Inject + private InternalBackupService internalBackupService; private final StateMachine2 _volStateMachine; protected List _storagePoolAllocators; @@ -312,7 +340,7 @@ public VolumeInfo moveVolume(VolumeInfo volumeInfo, long destPoolDcId, Long dest // Find a destination storage pool with the specified criteria DiskOffering diskOffering = _entityMgr.findById(DiskOffering.class, volumeInfo.getDiskOfferingId()); DiskProfile dskCh = new DiskProfile(volumeInfo.getId(), volumeInfo.getVolumeType(), volumeInfo.getName(), diskOffering.getId(), diskOffering.getDiskSize(), diskOffering.getTagsArray(), - diskOffering.isUseLocalStorage(), diskOffering.isRecreatable(), null, (diskOffering.getEncrypt() || volumeInfo.getPassphraseId() != null)); + diskOffering.isUseLocalStorage(), diskOffering.isRecreatable(), null, (diskOffering.getEncrypt() || volumeInfo.getPassphraseId() != null || volumeInfo.getKmsKeyId() != null)); dskCh.setHyperType(dataDiskHyperType); storageMgr.setDiskProfileThrottling(dskCh, null, diskOffering); @@ -347,9 +375,13 @@ public VolumeVO allocateDuplicateVolumeVO(Volume oldVol, DiskOffering diskOfferi newVol.setInstanceId(oldVol.getInstanceId()); newVol.setRecreatable(oldVol.isRecreatable()); newVol.setFormat(oldVol.getFormat()); - if ((diskOffering == null || diskOffering.getEncrypt()) && oldVol.getPassphraseId() != null) { - PassphraseVO passphrase = passphraseDao.persist(new PassphraseVO(true)); - newVol.setPassphraseId(passphrase.getId()); + if ((diskOffering == null || diskOffering.getEncrypt())) { + if (oldVol.getKmsKeyId() != null) { + newVol.setKmsKeyId(oldVol.getKmsKeyId()); + } else if (oldVol.getPassphraseId() != null) { + PassphraseVO passphrase = passphraseDao.persist(new PassphraseVO(true)); + newVol.setPassphraseId(passphrase.getId()); + } } return _volsDao.persist(newVol); @@ -504,7 +536,9 @@ public VolumeInfo createVolumeFromSnapshot(Volume volume, Snapshot snapshot, Use DiskOffering diskOffering = _entityMgr.findById(DiskOffering.class, volume.getDiskOfferingId()); if (diskOffering.getEncrypt()) { VolumeVO vol = (VolumeVO) volume; - volume = setPassphraseForVolumeEncryption(vol); + // Retrieve KMS key from volume's kmsKeyId if provided + KMSKeyVO kmsKey = getKmsKeyFromVolume(vol); + volume = setPassphraseForVolumeEncryption(vol, kmsKey, volume.getAccountId()); } DataCenter dc = _entityMgr.findById(DataCenter.class, volume.getDataCenterId()); DiskProfile dskCh = new DiskProfile(volume, diskOffering, snapshot.getHypervisorType()); @@ -624,13 +658,13 @@ public VolumeInfo createVolumeFromSnapshot(Volume volume, Snapshot snapshot, Use try { VolumeApiResult result = future.get(); if (result.isFailed()) { - String logMsg = String.format("Failed to create volume from snapshot [%s] due to [%s].", snapshotToString, result.getResult()); + String logMsg = String.format("Failed to create volume from Snapshot [%s] due to [%s].", snapshotToString, result.getResult()); logger.error(logMsg); throw new CloudRuntimeException(logMsg); } return result.getVolume(); } catch (InterruptedException | ExecutionException e) { - String message = String.format("Failed to create volume from snapshot [%s] due to [%s].", snapshotToString, e.getMessage()); + String message = String.format("Failed to create volume from Snapshot [%s] due to [%s].", snapshotToString, e.getMessage()); logger.error(message); logger.debug("Exception: ", e); throw new CloudRuntimeException(message, e); @@ -641,7 +675,7 @@ public VolumeInfo createVolumeFromSnapshot(Volume volume, Snapshot snapshot, Use } protected DiskProfile createDiskCharacteristics(VolumeInfo volumeInfo, VirtualMachineTemplate template, DataCenter dc, DiskOffering diskOffering) { - boolean requiresEncryption = diskOffering.getEncrypt() || volumeInfo.getPassphraseId() != null; + boolean requiresEncryption = diskOffering.getEncrypt() || volumeInfo.getPassphraseId() != null || volumeInfo.getKmsKeyId() != null; if (volumeInfo.getVolumeType() == Type.ROOT && Storage.ImageFormat.ISO != template.getFormat()) { String templateToString = getReflectOnlySelectedFields(template); String zoneToString = getReflectOnlySelectedFields(dc); @@ -721,7 +755,9 @@ public VolumeInfo createVolume(VolumeInfo volumeInfo, VirtualMachine vm, Virtual if (diskOffering.getEncrypt()) { VolumeVO vol = _volsDao.findById(volumeInfo.getId()); - setPassphraseForVolumeEncryption(vol); + // Retrieve KMS key from volume's kmsKeyId if provided + KMSKeyVO kmsKey = getKmsKeyFromVolume(vol); + setPassphraseForVolumeEncryption(vol, kmsKey, vol.getAccountId()); volumeInfo = volFactory.getVolume(volumeInfo.getId()); } } @@ -742,6 +778,17 @@ public VolumeInfo createVolume(VolumeInfo volumeInfo, VirtualMachine vm, Virtual logger.debug("Trying to create volume [{}] on storage pool [{}].", volumeToString, poolToString); DataStore store = dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary); + + // For CLVM pools, set the lock host hint so volume is created on the correct host + // This avoids the need for shared mode activation and improves performance + if (ClvmPoolManager.isClvmPoolType(pool.getPoolType()) && hostId != null) { + logger.info("CLVM pool detected. Setting lock host {} for volume {} to route creation to correct host", + hostId, volumeInfo.getUuid()); + volumeInfo.setDestinationHostId(hostId); + + clvmPoolManager.setClvmLockHostId(volumeInfo.getId(), hostId); + } + for (int i = 0; i < 2; i++) { // retry one more time in case of template reload is required for Vmware case AsyncCallFuture future = null; @@ -756,7 +803,7 @@ public VolumeInfo createVolume(VolumeInfo volumeInfo, VirtualMachine vm, Virtual VolumeApiResult result = future.get(); if (result.isFailed()) { if (result.getResult().contains(REQUEST_TEMPLATE_RELOAD) && (i == 0)) { - logger.debug("Retrying to deploy template [{}] for VMware, attempt 2/2. ", templateToString); + logger.debug("Retrying to deploy Template [{}] for VMware, attempt 2/2. ", templateToString); continue; } else { String msg = String.format("Failed to create volume [%s] due to [%s].", volumeToString, result.getResult()); @@ -772,7 +819,7 @@ public VolumeInfo createVolume(VolumeInfo volumeInfo, VirtualMachine vm, Virtual throw new CloudRuntimeException(msg, e); } } - throw new CloudRuntimeException(String.format("Failed to create volume [%s] even after retrying to deploy the template.", volumeToString)); + throw new CloudRuntimeException(String.format("Failed to create volume [%s] even after retrying to deploy the Template.", volumeToString)); } private String getReflectOnlySelectedFields(Object obj) { @@ -783,6 +830,122 @@ private String getVolumeIdentificationInfos(Volume volume) { return String.format("uuid: %s, name: %s", volume.getUuid(), volume.getName()); } + /** + * Updates the CLVM_LOCK_HOST_ID for a migrated volume if applicable. + * For CLVM volumes that are attached to a VM, this updates the lock host tracking + * to point to the VM's current host after volume migration. + * + * @param migratedVolume The volume that was migrated + * @param destPool The destination storage pool + * @param operationType Description of the operation (e.g., "migrated", "live-migrated") for logging + */ + private void updateClvmLockHostAfterMigration(Volume migratedVolume, StoragePool destPool, String operationType) { + if (migratedVolume == null || destPool == null) { + return; + } + + StoragePoolVO pool = _storagePoolDao.findById(destPool.getId()); + if (pool == null || !ClvmPoolManager.isClvmPoolType(pool.getPoolType())) { + return; + } + + if (migratedVolume.getInstanceId() == null) { + return; + } + + VMInstanceVO vm = vmInstanceDao.findById(migratedVolume.getInstanceId()); + if (vm == null || vm.getHostId() == null) { + return; + } + + clvmPoolManager.setClvmLockHostId(migratedVolume.getId(), vm.getHostId()); + logger.debug("Updated CLVM_LOCK_HOST_ID for {} volume {} to host {} where VM {} is running", + operationType, migratedVolume.getUuid(), vm.getHostId(), vm.getInstanceName()); + } + + /** + * Retrieves the CLVM lock host ID from any existing volume of the specified VM. + * This is useful when attaching a new volume to a stopped VM - we want to maintain + * consistency by using the same host that manages the VM's other CLVM volumes. + * + * @param vmId The ID of the VM + * @return The host ID if found, null otherwise + */ + private Long getClvmLockHostFromVmVolumes(Long vmId) { + if (vmId == null) { + return null; + } + + List vmVolumes = _volsDao.findByInstance(vmId); + if (vmVolumes == null || vmVolumes.isEmpty()) { + return null; + } + + for (VolumeVO volume : vmVolumes) { + if (volume.getPoolId() == null) { + continue; + } + + StoragePoolVO pool = _storagePoolDao.findById(volume.getPoolId()); + if (pool == null || !ClvmPoolManager.isClvmPoolType(pool.getPoolType())) { + continue; + } + Long lockHostId = clvmPoolManager.getClvmLockHostId( + volume.getId(), + volume.getUuid(), + volume.getPath(), + pool, + true + ); + if (lockHostId != null) { + logger.debug("Found actual CLVM lock host {} from volume {} of VM {} via LVM query", + lockHostId, volume.getUuid(), vmId); + return lockHostId; + } + } + + return null; + } + + private void transferClvmLocksForVmStart(List volumes, Long destHostId, VMInstanceVO vm) { + if (volumes == null || volumes.isEmpty() || destHostId == null) { + return; + } + + for (VolumeVO volume : volumes) { + if (volume.getPoolId() == null) { + continue; + } + + StoragePoolVO pool = _storagePoolDao.findById(volume.getPoolId()); + if (pool == null || !ClvmPoolManager.isClvmPoolType(pool.getPoolType())) { + continue; + } + + Long currentLockHost = clvmPoolManager.getClvmLockHostId( + volume.getId(), + volume.getUuid(), + volume.getPath(), + pool, + true + ); + + if (currentLockHost == null) { + clvmPoolManager.setClvmLockHostId(volume.getId(), destHostId); + } else if (!currentLockHost.equals(destHostId)) { + logger.info("CLVM volume {} is locked on host {} but VM {} starting on host {}. Transferring lock.", + volume.getUuid(), currentLockHost, vm.getInstanceName(), destHostId); + + if (!clvmPoolManager.transferClvmVolumeLock(volume.getUuid(), volume.getId(), + volume.getPath(), pool, currentLockHost, destHostId)) { + throw new CloudRuntimeException( + String.format("Failed to transfer CLVM lock for volume %s from host %s to host %s", + volume.getUuid(), currentLockHost, destHostId)); + } + } + } + } + public String getRandomVolumeName() { return UUID.randomUUID().toString(); } @@ -858,8 +1021,10 @@ protected DiskProfile toDiskProfile(Volume vol, DiskOffering offering) { @ActionEvent(eventType = EventTypes.EVENT_VOLUME_CREATE, eventDescription = "creating volume", create = true) @Override - public DiskProfile allocateRawVolume(Type type, String name, DiskOffering offering, Long size, Long minIops, Long maxIops, VirtualMachine vm, VirtualMachineTemplate template, Account owner, - Long deviceId) { + public DiskProfile allocateRawVolume( + Type type, String name, DiskOffering offering, Long size, Long minIops, Long maxIops, VirtualMachine vm, + VirtualMachineTemplate template, Account owner, Long deviceId, Long kmsKeyId, boolean incrementResourceCount + ) { if (size == null) { size = offering.getDiskSize(); } else { @@ -892,15 +1057,20 @@ public DiskProfile allocateRawVolume(Type type, String name, DiskOffering offeri vol.setDisplayVolume(userVm.isDisplayVm()); } + // Set KMS key ID if provided + if (kmsKeyId != null) { + vol.setKmsKeyId(kmsKeyId); + } + vol.setFormat(getSupportedImageFormatForCluster(vm.getHypervisorType())); vol = _volsDao.persist(vol); saveVolumeDetails(offering.getId(), vol.getId()); // Save usage event and update resource count for user vm volumes - if (vm.getType() == VirtualMachine.Type.User) { + if (vm.getType() == VirtualMachine.Type.User && incrementResourceCount) { UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, vol.getAccountId(), vol.getDataCenterId(), vol.getId(), vol.getName(), offering.getId(), null, size, - Volume.class.getName(), vol.getUuid(), vol.isDisplayVolume()); + Volume.class.getName(), vol.getUuid(), vol.getInstanceId(), vol.isDisplayVolume()); _resourceLimitMgr.incrementVolumeResourceCount(vm.getAccountId(), vol.isDisplayVolume(), vol.getSize(), offering); } DiskProfile diskProfile = toDiskProfile(vol, offering); @@ -911,7 +1081,7 @@ public DiskProfile allocateRawVolume(Type type, String name, DiskOffering offeri } 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, Volume volume, Snapshot snapshot) { + Account owner, long deviceId, String configurationId, Long kmsKeyId, Volume volume, Snapshot snapshot) { assert (template.getFormat() != ImageFormat.ISO) : "ISO is not a template."; if (volume != null) { @@ -961,6 +1131,11 @@ private DiskProfile allocateTemplatedVolume(Type type, String name, DiskOffering vol.setDisplayVolume(userVm.isDisplayVm()); } + // Set KMS key ID if provided + if (kmsKeyId != null) { + vol.setKmsKeyId(kmsKeyId); + } + vol = _volsDao.persist(vol); saveVolumeDetails(offering.getId(), vol.getId()); @@ -972,15 +1147,13 @@ private DiskProfile allocateTemplatedVolume(Type type, String name, DiskOffering // 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(); } UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, vol.getAccountId(), vol.getDataCenterId(), vol.getId(), vol.getName(), offeringId, vol.getTemplateId(), size, - Volume.class.getName(), vol.getUuid(), vol.isDisplayVolume()); + Volume.class.getName(), vol.getUuid(), vol.getInstanceId(), vol.isDisplayVolume()); _resourceLimitMgr.incrementVolumeResourceCount(vm.getAccountId(), vol.isDisplayVolume(), vol.getSize(), offering); } @@ -1052,7 +1225,7 @@ public void saveVolumeDetails(Long diskOfferingId, Long volumeId) { @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, Volume volume, Snapshot snapshot) { + Account owner, Long kmsKeyId, Volume volume, Snapshot snapshot) { String templateToString = getReflectOnlySelectedFields(template); int volumesNumber = 1; @@ -1099,7 +1272,7 @@ public List allocateTemplatedVolumes(Type type, String name, DiskOf } logger.info("Adding disk object [{}] to VM [{}]", volumeName, vm); DiskProfile diskProfile = allocateTemplatedVolume(type, volumeName, offering, volumeSize, minIops, maxIops, - template, vm, owner, deviceId, configurationId, volume, snapshot); + template, vm, owner, deviceId, configurationId, kmsKeyId, volume, snapshot); profiles.add(diskProfile); } @@ -1125,7 +1298,7 @@ private void updateRootDiskVolumeEventDetails(Type type, VirtualMachine vm, List callContext.setEventResourceId(volumeIds.get(0)); } String volumeUuids = volumeIds.stream().map(volumeId -> this._uuidMgr.getUuid(Volume.class, volumeId)).collect(Collectors.joining(", ")); - callContext.setEventDetails("Volume Type: " + type + "Volume Id: " + volumeUuids + " Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, vm.getId())); + callContext.setEventDetails("Volume Type: " + type + "Volume ID: " + volumeUuids + " Instance ID: " + vm.getUuid()); } } @@ -1189,24 +1362,42 @@ private VolumeInfo copyVolume(StoragePool rootDiskPool, VolumeInfo volumeInfo, V } @Override - public VolumeInfo createVolumeOnPrimaryStorage(VirtualMachine vm, VolumeInfo volumeInfo, HypervisorType rootDiskHyperType, StoragePool storagePool) throws NoTransitionException { + public VolumeInfo createVolumeOnPrimaryStorage(VirtualMachine vm, VolumeInfo volumeInfo, HypervisorType rootDiskHyperType, StoragePool storagePool, Long clusterId, Long podId) + throws NoTransitionException { String volumeToString = getReflectOnlySelectedFields(volumeInfo.getVolume()); VirtualMachineTemplate rootDiskTmplt = _entityMgr.findById(VirtualMachineTemplate.class, vm.getTemplateId()); DataCenter dcVO = _entityMgr.findById(DataCenter.class, vm.getDataCenterId()); - logger.trace("storage-pool {}/{} is associated with pod {}",storagePool.getName(), storagePool.getUuid(), storagePool.getPodId()); - Long podId = storagePool.getPodId() != null ? storagePool.getPodId() : vm.getPodIdToDeployIn(); + + if (storagePool != null) { + logger.trace("storage-pool {}/{} is associated with pod {}", storagePool.getName(), storagePool.getUuid(), storagePool.getPodId()); + podId = storagePool.getPodId() != null ? storagePool.getPodId() : vm.getPodIdToDeployIn(); + clusterId = storagePool.getClusterId(); + logger.trace("storage-pool {}/{} is associated with cluster {}",storagePool.getName(), storagePool.getUuid(), clusterId); + } + Pod pod = _entityMgr.findById(Pod.class, podId); ServiceOffering svo = _entityMgr.findById(ServiceOffering.class, vm.getServiceOfferingId()); DiskOffering diskVO = _entityMgr.findById(DiskOffering.class, volumeInfo.getDiskOfferingId()); - Long clusterId = storagePool.getClusterId(); - logger.trace("storage-pool {}/{} is associated with cluster {}",storagePool.getName(), storagePool.getUuid(), clusterId); + Long hostId = vm.getHostId(); - if (hostId == null && storagePool.isLocal()) { - List poolHosts = storagePoolHostDao.listByPoolId(storagePool.getId()); - if (poolHosts.size() > 0) { - hostId = poolHosts.get(0).getHostId(); + if (hostId == null && storagePool != null && (storagePool.isLocal() || ClvmPoolManager.isClvmPoolType(storagePool.getPoolType()))) { + if (ClvmPoolManager.isClvmPoolType(storagePool.getPoolType())) { + hostId = getClvmLockHostFromVmVolumes(vm.getId()); + if (hostId != null) { + logger.debug("Using CLVM lock host {} from VM {}'s existing volumes for new volume creation", + hostId, vm.getUuid()); + } + } + + if (hostId == null) { + List poolHosts = storagePoolHostDao.listByPoolId(storagePool.getId()); + if (!poolHosts.isEmpty()) { + hostId = poolHosts.get(0).getHostId(); + logger.debug("Selected host {} from storage pool {} for stopped VM {} volume creation", + hostId, storagePool.getUuid(), vm.getUuid()); + } } } @@ -1365,7 +1556,7 @@ private void destroyVolumeInContext(Volume volume) { // Create new context and inject correct event resource type, id and details, // otherwise VOLUME.DESTROY event will be associated with VirtualMachine and contain VM id and other information. CallContext volumeContext = CallContext.register(CallContext.current(), ApiCommandResourceType.Volume); - volumeContext.setEventDetails("Volume Type: " + volume.getVolumeType() + " Volume Id: " + volume.getUuid() + " Vm Id: " + _uuidMgr.getUuid(VirtualMachine.class, volume.getInstanceId())); + volumeContext.setEventDetails("Volume Type: " + volume.getVolumeType() + " Volume ID: " + volume.getUuid() + " Instance ID: " + _uuidMgr.getUuid(VirtualMachine.class, volume.getInstanceId())); volumeContext.setEventResourceType(ApiCommandResourceType.Volume); volumeContext.setEventResourceId(volume.getId()); try { @@ -1422,7 +1613,7 @@ public Volume migrateVolume(Volume volume, StoragePool destPool) throws StorageU String volumeToString = getVolumeIdentificationInfos(volume); VolumeInfo vol = volFactory.getVolume(volume.getId()); - if (vol == null){ + if (vol == null) { throw new CloudRuntimeException(String.format("Volume migration failed because volume [%s] is null.", volumeToString)); } if (destPool == null) { @@ -1451,6 +1642,10 @@ public Volume migrateVolume(Volume volume, StoragePool destPool) throws StorageU _snapshotDao.updateVolumeIds(vol.getId(), result.getVolume().getId()); _snapshotDataStoreDao.updateVolumeIds(vol.getId(), result.getVolume().getId()); } + internalBackupService.updateVolumeId(vol.getId(), result.getVolume().getId()); + + // For CLVM volumes attached to a VM, update the CLVM_LOCK_HOST_ID after migration + updateClvmLockHostAfterMigration(result.getVolume(), destPool, "migrated"); } return result.getVolume(); } catch (InterruptedException | ExecutionException e) { @@ -1476,6 +1671,10 @@ public Volume liveMigrateVolume(Volume volume, StoragePool destPool) { logger.error("Volume [{}] migration failed due to [{}].", volToString, result.getResult()); return null; } + + // For CLVM volumes attached to a VM, update the CLVM_LOCK_HOST_ID after live migration + updateClvmLockHostAfterMigration(result.getVolume(), destPool, "live-migrated"); + return result.getVolume(); } catch (InterruptedException | ExecutionException e) { logger.error("Volume [{}] migration failed due to [{}].", volToString, e.getMessage()); @@ -1500,13 +1699,15 @@ public void migrateVolumes(VirtualMachine vm, VirtualMachineTO vmTo, Host srcHos String storagePoolToString = getReflectOnlySelectedFields(storagePool); if (volume.getInstanceId() != vm.getId()) { - throw new CloudRuntimeException(String.format("Volume [%s] that has to be migrated, but it doesn't belong to the instance [%s].", volumeToString, vm)); + throw new CloudRuntimeException(String.format("Volume [%s] that has to be migrated, but it doesn't belong to the Instance [%s].", volumeToString, vm)); } if (destPool == null) { throw new CloudRuntimeException(String.format("Failed to find the destination storage pool [%s] to migrate the volume [%s] to.", storagePoolToString, volumeToString)); } + internalBackupService.prepareVolumeForMigration(volume); + volumeMap.put(volFactory.getVolume(volume.getId()), (DataStore)destPool); } @@ -1518,6 +1719,22 @@ public void migrateVolumes(VirtualMachine vm, VirtualMachineTO vmTo, Host srcHos logger.error(msg); throw new CloudRuntimeException(msg); } + for (Map.Entry entry : volumeToPool.entrySet()) { + Volume volume = entry.getKey(); + StoragePool destPool = entry.getValue(); + StoragePoolVO srcPool = _storagePoolDao.findById(volume.getPoolId()); + if (srcPool != null && srcPool.getId() == destPool.getId() && + ClvmPoolManager.isClvmPoolType(srcPool.getPoolType())) { + if (!clvmPoolManager.transferClvmVolumeLock(volume.getUuid(), volume.getId(), + volume.getPath(), srcPool, srcHost.getId(), destHost.getId())) { + throw new CloudRuntimeException(String.format( + "Failed to transfer CLVM lock for volume [%s] to destination host [%s].", + volume.getUuid(), destHost.getId())); + } + } else { + updateClvmLockHostAfterMigration(volume, destPool, "vm-migrated"); + } + } } catch (InterruptedException | ExecutionException e) { logger.error("Failed to migrate VM [{}] along with its volumes due to [{}].", vm, e.getMessage()); logger.debug("Exception: ", e); @@ -1582,12 +1799,8 @@ public void prepareForMigration(VirtualMachineProfile vm, DeployDestination dest vm.addDisk(disk); } - //if (vm.getType() == VirtualMachine.Type.User && vm.getTemplate().getFormat() == ImageFormat.ISO) { if (vm.getType() == VirtualMachine.Type.User) { _tmpltMgr.prepareIsoForVmProfile(vm, dest); - //DataTO dataTO = tmplFactory.getTemplate(vm.getTemplate().getId(), DataStoreRole.Image, vm.getVirtualMachine().getDataCenterId()).getTO(); - //DiskTO iso = new DiskTO(dataTO, 3L, null, Volume.Type.ISO); - //vm.addDisk(iso); } } @@ -1778,7 +1991,9 @@ private Pair recreateVolume(VolumeVO vol, VirtualMachinePro if (vol.getState() == Volume.State.Allocated || vol.getState() == Volume.State.Creating) { DiskOffering diskOffering = _entityMgr.findById(DiskOffering.class, vol.getDiskOfferingId()); if (diskOffering.getEncrypt()) { - vol = setPassphraseForVolumeEncryption(vol); + // Retrieve KMS key from volume's kmsKeyId if provided + KMSKeyVO kmsKey = getKmsKeyFromVolume(vol); + vol = setPassphraseForVolumeEncryption(vol, kmsKey, vol.getAccountId()); } newVol = vol; } else { @@ -1854,6 +2069,19 @@ private Pair recreateVolume(VolumeVO vol, VirtualMachinePro future = volService.createManagedStorageVolumeFromTemplateAsync(volume, destPool.getId(), templ, hostId); } else { + // For CLVM pools, set the destination host hint so volume is created on the correct host + // This avoids the need for shared mode activation and improves performance + StoragePoolVO poolVO = _storagePoolDao.findById(destPool.getId()); + if (poolVO != null && ClvmPoolManager.isClvmPoolType(poolVO.getPoolType())) { + Long hostId = vm.getVirtualMachine().getHostId(); + if (hostId != null) { + volume.setDestinationHostId(hostId); + clvmPoolManager.setClvmLockHostId(volume.getId(), hostId); + logger.info("CLVM pool detected during volume creation from template. Setting lock host {} for volume {} (persisted to DB) to route creation to correct host", + hostId, volume.getUuid()); + } + } + future = volService.createVolumeFromTemplateAsync(volume, destPool.getId(), templ); } } @@ -1901,16 +2129,72 @@ private Pair recreateVolume(VolumeVO vol, VirtualMachinePro return new Pair<>(newVol, destPool); } - private VolumeVO setPassphraseForVolumeEncryption(VolumeVO volume) { - if (volume.getPassphraseId() != null) { + /** + * Helper method to retrieve KMS key from volume's kmsKeyId + */ + private KMSKeyVO getKmsKeyFromVolume(VolumeVO volume) { + if (volume.getKmsKeyId() == null) { + return null; + } + return kmsKeyDao.findById(volume.getKmsKeyId()); + } + + private VolumeVO setKmsKeyForVolumeEncryption(VolumeVO volume, KMSKeyVO kmsKey, Long callerAccountId) { + // Determine caller account ID if not provided + if (callerAccountId == null) { + callerAccountId = volume.getAccountId(); + } + + // Validate permission + if (!kmsManager.hasPermission(callerAccountId, kmsKey)) { + throw new CloudRuntimeException("No permission to use KMS key: " + kmsKey); + } + + try { + logger.debug("Generating and wrapping DEK for volume {} using KMS key {}", volume.getName(), kmsKey.getUuid()); + long startTime = System.currentTimeMillis(); + + // Generate and wrap DEK using active KEK version + WrappedKey wrappedKey = kmsManager.generateVolumeKeyWithKek(kmsKey, callerAccountId); + + // The wrapped key is already persisted by generateVolumeKeyWithKek, get its ID + KMSWrappedKeyVO wrappedKeyVO = kmsWrappedKeyDao.findByUuid(wrappedKey.getUuid()); + if (wrappedKeyVO == null) { + throw new CloudRuntimeException("Failed to find persisted wrapped key: " + wrappedKey.getUuid()); + } + + // Set the wrapped key ID on the volume + volume.setKmsWrappedKeyId(wrappedKeyVO.getId()); + + long finishTime = System.currentTimeMillis(); + logger.debug("Generating and persisting wrapped key took {} ms for volume: {}", + (finishTime - startTime), volume.getName()); + + return _volsDao.persist(volume); + + } catch (KMSException e) { + throw new CloudRuntimeException("KMS failure while setting up volume encryption: " + e.getMessage(), e); + } + } + private VolumeVO setPassphraseForVolumeEncryption(VolumeVO volume, KMSKeyVO kmsKey, Long callerAccountId) { + // If volume already has encryption set up, return it + if (volume.getKmsWrappedKeyId() != null || volume.getPassphraseId() != null) { return volume; } - logger.debug("Creating passphrase for the volume: " + volume.getName()); + if (kmsKey != null) { + return setKmsKeyForVolumeEncryption(volume, kmsKey, callerAccountId); + } + // Legacy: passphrase-based encryption (fallback when KMS not enabled or KMS key not specified) + return setPassphraseForVolumeEncryption(volume); + } + + private VolumeVO setPassphraseForVolumeEncryption(VolumeVO volume) { + logger.debug("Creating passphrase for the volume: {}", volume.getName()); long startTime = System.currentTimeMillis(); PassphraseVO passphrase = passphraseDao.persist(new PassphraseVO(true)); volume.setPassphraseId(passphrase.getId()); long finishTime = System.currentTimeMillis(); - logger.debug("Creating and persisting passphrase took: " + (finishTime - startTime) + " ms for the volume: " + volume.toString()); + logger.debug("Creating and persisting passphrase took: {} ms for the volume: {}", finishTime - startTime, volume.toString()); return _volsDao.persist(volume); } @@ -1939,22 +2223,34 @@ protected void updateVolumeSize(DataStore store, VolumeVO vol) throws ResourceAl PrimaryDataStoreDriver driver = (PrimaryDataStoreDriver) store.getDriver(); long newSize = driver.getVolumeSizeRequiredOnPool(vol.getSize(), template == null ? null : template.getSize(), - vol.getPassphraseId() != null); - - 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); + vol.getPassphraseId() != null || vol.getKmsKeyId() != null); + + if (newSize == vol.getSize()) { + return; + } + + DiskOfferingVO diskOffering = diskOfferingDao.findByIdIncludingRemoved(vol.getDiskOfferingId()); + + List reservations = new ArrayList<>(); + try { + 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, reservations); + _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); + } finally { + ReservationHelper.closeAll(reservations); } + vol.setSize(newSize); + _volsDao.persist(vol); } @Override @@ -1965,13 +2261,18 @@ public void prepare(VirtualMachineProfile vm, DeployDestination dest) throws Sto throw new CloudRuntimeException(msg); } - // don't allow to start vm that doesn't have a root volume if (_volsDao.findByInstanceAndType(vm.getId(), Volume.Type.ROOT).isEmpty()) { throw new CloudRuntimeException(String.format("ROOT volume is missing, unable to prepare volumes for the VM [%s].", vm.getVirtualMachine())); } List vols = _volsDao.findUsableVolumesForInstance(vm.getId()); + VirtualMachine vmInstance = vm.getVirtualMachine(); + VMInstanceVO vmInstanceVO = vmInstanceDao.findById(vmInstance.getId()); + if (vmInstance.getState() == State.Starting && dest.getHost() != null) { + transferClvmLocksForVmStart(vols, dest.getHost().getId(), vmInstanceVO); + } + List tasks = getTasks(vols, dest.getStorageForDisks(), vm); Volume vol = null; PrimaryDataStore store; @@ -2243,7 +2544,8 @@ public void destroyVolume(Volume volume) { if (volume.getState() == Volume.State.Allocated) { _volsDao.remove(volume.getId()); stateTransitTo(volume, Volume.Event.DestroyRequested); - _resourceLimitMgr.decrementVolumeResourceCount(volume.getAccountId(), volume.isDisplay(), volume.getSize(), diskOfferingDao.findByIdIncludingRemoved(volume.getDiskOfferingId())); + _resourceLimitMgr.decrementVolumeResourceCount(volume.getAccountId(), volume.isDisplay(), volume.getSize(), diskOfferingDao.findByIdIncludingRemoved(volume.getDiskOfferingId()), + null); } else { destroyVolumeInContext(volume); } @@ -2303,6 +2605,7 @@ public void updateVolumeDiskChain(long volumeId, String path, String chainInfo, StoragePoolVO pool = _storagePoolDao.findByUuid(updatedDataStoreUUID); if (pool != null) { vol.setPoolId(pool.getId()); + vol.setPoolType(pool.getPoolType()); } } _volsDao.update(volumeId, vol); @@ -2312,7 +2615,7 @@ public void updateVolumeDiskChain(long volumeId, String path, String chainInfo, @Override public DiskProfile importVolume(Type type, String name, DiskOffering offering, Long sizeInBytes, Long minIops, Long maxIops, Long zoneId, HypervisorType hypervisorType, VirtualMachine vm, VirtualMachineTemplate template, Account owner, - Long deviceId, Long poolId, String path, String chainInfo) { + Long deviceId, Long poolId, Storage.StoragePoolType poolType, String path, String chainInfo) { if (sizeInBytes == null) { sizeInBytes = offering.getDiskSize(); } @@ -2353,6 +2656,7 @@ public DiskProfile importVolume(Type type, String name, DiskOffering offering, L vol.setFormat(getSupportedImageFormatForCluster(hypervisorType)); vol.setPoolId(poolId); + vol.setPoolType(poolType); vol.setPath(path); vol.setChainInfo(chainInfo); vol.setState(Volume.State.Ready); @@ -2362,7 +2666,7 @@ public DiskProfile importVolume(Type type, String name, DiskOffering offering, L @Override public DiskProfile updateImportedVolume(Type type, DiskOffering offering, VirtualMachine vm, VirtualMachineTemplate template, - Long deviceId, Long poolId, String path, String chainInfo, DiskProfile diskProfile) { + Long deviceId, Long poolId, Storage.StoragePoolType poolType, String path, String chainInfo, DiskProfile diskProfile) { VolumeVO vol = _volsDao.findById(diskProfile.getVolumeId()); if (vm != null) { @@ -2396,6 +2700,7 @@ public DiskProfile updateImportedVolume(Type type, DiskOffering offering, Virtua vol.setFormat(getSupportedImageFormatForCluster(vm.getHypervisorType())); vol.setPoolId(poolId); + vol.setPoolType(poolType); vol.setPath(path); vol.setChainInfo(chainInfo); vol.setSize(diskProfile.getSize()); diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/service/api/ProvisioningServiceImpl.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/service/api/ProvisioningServiceImpl.java index 51e87663919b..ff75aa0cbb65 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/service/api/ProvisioningServiceImpl.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/service/api/ProvisioningServiceImpl.java @@ -140,20 +140,12 @@ public List listHosts() { @Override public List listPods() { - /* - * Not in use now, just commented out. - */ - //List pods = new ArrayList(); - //pods.add(new PodEntityImpl("pod-uuid-1", "pod1")); - //pods.add(new PodEntityImpl("pod-uuid-2", "pod2")); return null; } @Override public List listZones() { List zones = new ArrayList(); - //zones.add(new ZoneEntityImpl("zone-uuid-1")); - //zones.add(new ZoneEntityImpl("zone-uuid-2")); return zones; } diff --git a/engine/orchestration/src/main/resources/META-INF/cloudstack/core/spring-engine-orchestration-core-context.xml b/engine/orchestration/src/main/resources/META-INF/cloudstack/core/spring-engine-orchestration-core-context.xml index 17c5002c718b..e17302e68a1f 100644 --- a/engine/orchestration/src/main/resources/META-INF/cloudstack/core/spring-engine-orchestration-core-context.xml +++ b/engine/orchestration/src/main/resources/META-INF/cloudstack/core/spring-engine-orchestration-core-context.xml @@ -44,6 +44,8 @@ value="#{storagePoolAllocatorsRegistry.registered}" /> + + + + 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 fb42f2477888..43d83a672c0f 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 @@ -22,6 +22,7 @@ import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupRoutingCommand; import com.cloud.exception.ConnectionException; +import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.Status; import com.cloud.host.dao.HostDao; @@ -104,4 +105,36 @@ public void testGetTimeoutWithGranularTimeout() { Assert.assertEquals(50, result); } + + @Test + public void testGetHostSshPortWithHostNull() { + int hostSshPort = mgr.getHostSshPort(null); + Assert.assertEquals(22, hostSshPort); + } + + @Test + public void testGetHostSshPortWithNonKVMHost() { + HostVO host = Mockito.mock(HostVO.class); + Mockito.when(host.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.XenServer); + int hostSshPort = mgr.getHostSshPort(host); + Assert.assertEquals(22, hostSshPort); + } + + @Test + public void testGetHostSshPortWithKVMHostDefaultPort() { + HostVO host = Mockito.mock(HostVO.class); + Mockito.when(host.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + Mockito.when(host.getClusterId()).thenReturn(1L); + int hostSshPort = mgr.getHostSshPort(host); + Assert.assertEquals(22, hostSshPort); + } + + @Test + public void testGetHostSshPortWithKVMHostCustomPort() { + HostVO host = Mockito.mock(HostVO.class); + Mockito.when(host.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + Mockito.when(host.getDetail(Host.HOST_SSH_PORT)).thenReturn(String.valueOf(3922)); + int hostSshPort = mgr.getHostSshPort(host); + Assert.assertEquals(3922, hostSshPort); + } } 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 4f6329f81cb8..c656e1a282af 100644 --- a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java +++ b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java @@ -19,14 +19,18 @@ 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.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyList; 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.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -34,8 +38,11 @@ import static org.mockito.Mockito.when; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -43,15 +50,30 @@ import java.util.UUID; import java.util.stream.Collectors; +import com.cloud.agent.api.UnmanageInstanceAnswer; +import com.cloud.agent.api.UnmanageInstanceCommand; +import com.cloud.agent.api.to.DataTO; +import com.cloud.agent.api.to.DiskTO; +import com.cloud.api.ApiDBUtils; +import com.cloud.event.ActionEventUtils; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.ha.HighAvailabilityManager; +import com.cloud.network.Network; +import com.cloud.network.NetworkModel; import com.cloud.resource.ResourceManager; +import com.cloud.storage.clvm.ClvmPoolManager; 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.engine.subsystem.api.storage.VolumeDataFactory; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; 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.framework.jobs.dao.VmWorkJobDao; +import org.apache.cloudstack.framework.jobs.impl.VmWorkJobVO; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.to.VolumeObjectTO; @@ -61,6 +83,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.InjectMocks; import org.mockito.Mock; @@ -167,6 +190,9 @@ public class VirtualMachineManagerImplTest { private PrimaryDataStoreDao storagePoolDaoMock; @Mock private VMInstanceVO vmInstanceMock; + @Mock + private VmWorkJobDao _workJobDao; + private long vmInstanceVoMockId = 1L; @Mock @@ -181,6 +207,9 @@ public class VirtualMachineManagerImplTest { private long hostMockId = 1L; private long clusterMockId = 2L; private long zoneMockId = 3L; + private final String vmMockUuid = UUID.randomUUID().toString(); + private final String hostUuid = UUID.randomUUID().toString(); + @Mock private HostVO hostMock; @Mock @@ -192,6 +221,7 @@ public class VirtualMachineManagerImplTest { private StoragePoolVO storagePoolVoMock; private long storagePoolVoMockId = 11L; private long storagePoolVoMockClusterId = 234L; + private String vmName = "vm1"; @Mock private VolumeVO volumeVoMock; @@ -254,6 +284,16 @@ public class VirtualMachineManagerImplTest { NicDao _nicsDao; @Mock NetworkService networkService; + @Mock + NetworkModel networkModel; + @Mock + VolumeDataFactory volumeDataFactoryMock; + @Mock + StorageManager storageManager; + @Mock + private HighAvailabilityManager _haMgr; + @Mock + VirtualMachineGuru guru; private ConfigDepotImpl configDepotImpl; private boolean updatedConfigKeyDepot = false; @@ -263,6 +303,7 @@ public void setup() { ReflectionTestUtils.getField(VirtualMachineManager.VmMetadataManufacturer, "s_depot"); virtualMachineManagerImpl.setHostAllocators(new ArrayList<>()); + when(vmInstanceMock.getName()).thenReturn(vmName); when(vmInstanceMock.getId()).thenReturn(vmInstanceVoMockId); when(vmInstanceMock.getServiceOfferingId()).thenReturn(2L); when(hostMock.getId()).thenReturn(hostMockId); @@ -297,6 +338,19 @@ public void cleanup() { } } + private ClvmPoolManager injectMockedClvmPoolManager() { + ClvmPoolManager clvmPoolManagerMock = mock(ClvmPoolManager.class); + ReflectionTestUtils.setField(virtualMachineManagerImpl, "clvmPoolManager", clvmPoolManagerMock); + return clvmPoolManagerMock; + } + + private Method getUpdateClvmLockHostForVmVolumesMethod() throws NoSuchMethodException { + Method method = VirtualMachineManagerImpl.class.getDeclaredMethod( + "updateClvmLockHostForVmVolumes", long.class, long.class); + method.setAccessible(true); + return method; + } + @Test public void testaddHostIpToCertDetailsIfConfigAllows() { Host vmHost = mock(Host.class); @@ -1361,7 +1415,7 @@ public void recreateCheckpointsKvmOnVmAfterMigrationTestAgentUnavailableThrowsCl 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()); + doThrow(new AgentUnavailableException(0)).when(agentManagerMock).send(Mockito.anyLong(), (Command) any()); Mockito.doNothing().when(snapshotManagerMock).endSnapshotChainForVolume(Mockito.anyLong(), Mockito.any()); virtualMachineManagerImpl.recreateCheckpointsKvmOnVmAfterMigration(vmInstanceMock, 0); @@ -1374,7 +1428,7 @@ public void recreateCheckpointsKvmOnVmAfterMigrationTestOperationTimedoutExcepti 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()); + 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); @@ -1641,4 +1695,455 @@ public void processPrepareExternalProvisioning_externalHypervisor_sendsCommand() virtualMachineManagerImpl.processPrepareExternalProvisioning(true, host, vmProfile, mock(DataCenter.class)); verify(agentManagerMock).send(anyLong(), any(Command.class)); } + + @Test + public void testPrepVMSpecForUnmanageInstance() { + // Arrange + final Long accountId = 1L; + final Long offeringId = 1L; + final Long templateId = 1L; + + // Mock vm + VMInstanceVO vm = Mockito.mock(VMInstanceVO.class); + when(vm.getId()).thenReturn(vmInstanceVoMockId); + when(vm.getAccountId()).thenReturn(accountId); + when(vm.getServiceOfferingId()).thenReturn(offeringId); + when(vm.getTemplateId()).thenReturn(templateId); + when(vm.getHypervisorType()).thenReturn(HypervisorType.KVM); + when(vmInstanceDaoMock.findById(vmInstanceVoMockId)).thenReturn(vm); + + // Mock owner + AccountVO owner = Mockito.mock(AccountVO.class); + when(_entityMgr.findById(Account.class, accountId)).thenReturn(owner); + + ServiceOfferingVO offering = Mockito.mock(ServiceOfferingVO.class); + when(serviceOfferingDaoMock.findById(vmInstanceVoMockId, offeringId)).thenReturn(offering); + + VMTemplateVO template = Mockito.mock(VMTemplateVO.class); + when(_entityMgr.findByIdIncludingRemoved(VirtualMachineTemplate.class, templateId)).thenReturn(template); + + when(hostMock.getClusterId()).thenReturn(clusterMockId); + + // Mock cpuOvercommitRatio and ramOvercommitRatio + ClusterDetailsVO cpuOvercommitRatio = Mockito.mock(ClusterDetailsVO.class); + when(cpuOvercommitRatio.getValue()).thenReturn("1.0"); + when(_clusterDetailsDao.findDetail(clusterMockId, VmDetailConstants.CPU_OVER_COMMIT_RATIO)).thenReturn(cpuOvercommitRatio); + ClusterDetailsVO ramOvercommitRatio = Mockito.mock(ClusterDetailsVO.class); + when(ramOvercommitRatio.getValue()).thenReturn("1.0"); + when(_clusterDetailsDao.findDetail(clusterMockId, VmDetailConstants.MEMORY_OVER_COMMIT_RATIO)).thenReturn(ramOvercommitRatio); + + // Mock NICs + List nics = new ArrayList<>(); + NicVO nic1 = Mockito.mock(NicVO.class); + when(nic1.getDeviceId()).thenReturn(1); + nics.add(nic1); + NicVO nic2 = Mockito.mock(NicVO.class); + when(nic2.getDeviceId()).thenReturn(0); + nics.add(nic2); + when(_nicsDao.listByVmId(vmInstanceVoMockId)).thenReturn(nics); + + Network networkMock = Mockito.mock(Network.class); + when(networkModel.getNetwork(anyLong())).thenReturn(networkMock); + + when(volumeVoMock.getVolumeType()).thenReturn(Volume.Type.ROOT); + when(volumeVoMock.getDeviceId()).thenReturn(0L); + when(volumeVoMock.getPath()).thenReturn("/"); + when(volumeVoMock.getDiskOfferingId()).thenReturn(1L); + when(volumeDaoMock.findUsableVolumesForInstance(vmInstanceVoMockId)).thenReturn(List.of(volumeVoMock)); + + VolumeInfo volumeInfo = mock(VolumeInfo.class); + DataTO dataTO = mock(DataTO.class); + when(volumeInfo.getTO()).thenReturn(dataTO); + when(volumeDataFactoryMock.getVolume(anyLong())).thenReturn(volumeInfo); + when(storageManager.getDiskWithThrottling(any(), any(), anyLong(), anyString(), anyLong(), anyLong())).thenReturn(Mockito.mock(DiskTO.class)); + + Map details = new HashMap<>(); + details.put(VirtualMachineProfile.Param.BootType.getName(), "BIOS"); + details.put(VirtualMachineProfile.Param.BootMode.getName(), "LEGACY"); + details.put(VirtualMachineProfile.Param.UefiFlag.getName(), "Yes"); + when(vmInstanceDetailsDao.listDetailsKeyPairs(anyLong(), anyList())).thenReturn(details); + + com.cloud.hypervisor.HypervisorGuru guru = Mockito.mock(com.cloud.hypervisor.HypervisorGuru.class); + when(_hvGuruMgr.getGuru(HypervisorType.KVM)).thenReturn(guru); + VirtualMachineTO vmTO = new VirtualMachineTO() {}; + when(guru.implement(any(VirtualMachineProfile.class))).thenAnswer((Answer) invocation -> { + VirtualMachineProfile profile = invocation.getArgument(0); + assertEquals("BIOS", profile.getParameter(VirtualMachineProfile.Param.BootType)); + return vmTO; + }); + + // Act + VirtualMachineTO result = virtualMachineManagerImpl.prepVmSpecForUnmanageCmd(vmInstanceVoMockId, hostMockId); + + // Assert + assertNotNull(result); + assertEquals(vmTO, result); + verify(_clusterDetailsDao, times(2)).findDetail(eq(clusterMockId), anyString()); + verify(vmInstanceDetailsDao).listDetailsKeyPairs(anyLong(), anyList()); + } + + @Test + public void testPersistDomainForKvmForRunningVmSuccess() throws AgentUnavailableException, OperationTimedoutException { + when(vmInstanceMock.getState()).thenReturn(VirtualMachine.State.Running); + when(vmInstanceMock.getHostId()).thenReturn(hostMockId); + UnmanageInstanceAnswer successAnswer = new UnmanageInstanceAnswer(null, true, "success"); + when(agentManagerMock.send(anyLong(), any(Command.class))).thenReturn(successAnswer); + virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock, null); + ArgumentCaptor hostIdCaptor = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor commandCaptor = ArgumentCaptor.forClass(UnmanageInstanceCommand.class); + verify(agentManagerMock).send(hostIdCaptor.capture(), commandCaptor.capture()); + assertEquals(hostMockId, hostIdCaptor.getValue().longValue()); + } + + @Test + public void testPersistDomainForKvmForStoppedVmSuccess() throws AgentUnavailableException, OperationTimedoutException { + when(vmInstanceMock.getState()).thenReturn(VirtualMachine.State.Stopped); + VirtualMachineTO vmTO = new VirtualMachineTO() {}; + vmTO.setName(vmName); + doReturn(vmTO).when(virtualMachineManagerImpl).prepVmSpecForUnmanageCmd(vmInstanceVoMockId, 1L); + UnmanageInstanceAnswer successAnswer = new UnmanageInstanceAnswer(null, true, "success"); + when(agentManagerMock.send(anyLong(), any(UnmanageInstanceCommand.class))).thenReturn(successAnswer); + when(virtualMachineManagerImpl.findClusterAndHostIdForVm(vmInstanceMock, false)).thenReturn(new Pair<>(clusterMockId, hostMockId)); + virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock, null); + ArgumentCaptor hostIdCaptor = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor commandCaptor = ArgumentCaptor.forClass(UnmanageInstanceCommand.class); + verify(agentManagerMock).send(hostIdCaptor.capture(), commandCaptor.capture()); + assertEquals(1L, hostIdCaptor.getValue().longValue()); + UnmanageInstanceCommand sentCommand = commandCaptor.getValue(); + assertNotNull(sentCommand.getVm()); + assertEquals(vmTO, sentCommand.getVm()); + assertEquals(vmName, sentCommand.getInstanceName()); + verify(virtualMachineManagerImpl).prepVmSpecForUnmanageCmd(vmInstanceVoMockId, 1L); + } + + + @Test + public void testPersistDomainForKvmForStoppedVmNoHost() { + when(vmInstanceMock.getState()).thenReturn(VirtualMachine.State.Stopped); + VirtualMachineTO vmTO = new VirtualMachineTO() {}; + vmTO.setName(vmName); + when(virtualMachineManagerImpl.findClusterAndHostIdForVm(vmInstanceMock, false)).thenReturn(new Pair<>(clusterMockId, null)); + CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, () -> virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock, null)); + assertEquals("No available host to persist domain XML for Instance: " + vmName, exception.getMessage()); + } + + @Test + public void testPersistDomainForKvmForRunningVmAgentFailure() throws AgentUnavailableException, OperationTimedoutException { + when(vmInstanceMock.getState()).thenReturn(VirtualMachine.State.Running); + when(vmInstanceMock.getHostId()).thenReturn(hostMockId); + UnmanageInstanceAnswer failureAnswer = new UnmanageInstanceAnswer(null, false, "failure"); + when(agentManagerMock.send(anyLong(), any(UnmanageInstanceCommand.class))).thenReturn(failureAnswer); + CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, () -> virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock, null)); + assertEquals("Failed to persist domain XML for Instance: " + vmName + " on host ID: " + hostMockId, exception.getMessage()); + } + + @Test + public void testPersistDomainForKvmAgentUnavailable() throws AgentUnavailableException, OperationTimedoutException { + when(vmInstanceMock.getState()).thenReturn(VirtualMachine.State.Running); + when(vmInstanceMock.getHostId()).thenReturn(hostMockId); + doThrow(new AgentUnavailableException("Agent down", hostMockId)).when(agentManagerMock).send(anyLong(), any(UnmanageInstanceCommand.class)); + CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, () -> virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock, null)); + assertEquals("Failed to send command to persist domain XML for Instance: " + vmName + " on host ID: " + hostMockId, exception.getMessage()); + } + + @Test(expected = ConcurrentOperationException.class) + public void testUnmanagePendingHaWork() { + when(vmInstanceDaoMock.findByUuid(vmMockUuid)).thenReturn(vmInstanceMock); + when(_workJobDao.listPendingWorkJobs(VirtualMachine.Type.Instance, vmInstanceVoMockId)).thenReturn(Collections.emptyList()); + when(_haMgr.hasPendingHaWork(vmInstanceVoMockId)).thenReturn(true); + virtualMachineManagerImpl.unmanage(vmMockUuid, null); + } + + @Test + public void testPersistDomainForKvmOperationTimedOut() throws AgentUnavailableException, OperationTimedoutException { + when(vmInstanceMock.getState()).thenReturn(VirtualMachine.State.Running); + when(vmInstanceMock.getHostId()).thenReturn(hostMockId); + doThrow(new OperationTimedoutException(null, hostMockId, 123L, 60, false)).when(agentManagerMock).send(anyLong(), any(UnmanageInstanceCommand.class)); + CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, () -> virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock, null)); + assertEquals("Failed to send command to persist domain XML for Instance: " + vmName + " on host ID: " + hostMockId, exception.getMessage()); + } + + @Test(expected = CloudRuntimeException.class) + public void testUnmanageVmRemoved() { + when(vmInstanceMock.getRemoved()).thenReturn(new Date()); + when(vmInstanceDaoMock.findByUuid(vmMockUuid)).thenReturn(vmInstanceMock); + virtualMachineManagerImpl.unmanage(vmMockUuid, null); + } + + @Test(expected = ConcurrentOperationException.class) + public void testUnmanagePendingWorkJobs() { + when(vmInstanceDaoMock.findByUuid(vmMockUuid)).thenReturn(vmInstanceMock); + List pendingJobs = new ArrayList<>(); + VmWorkJobVO vmWorkJobVO = mock(VmWorkJobVO.class); + pendingJobs.add(vmWorkJobVO); + when(_workJobDao.listPendingWorkJobs(VirtualMachine.Type.Instance, vmInstanceVoMockId)).thenReturn(pendingJobs); + virtualMachineManagerImpl.unmanage(vmMockUuid, null); + } + + @Test + public void testUnmanageHostNotFoundAfterTransaction() { + when(vmInstanceMock.getHostId()).thenReturn(hostMockId); + when(vmInstanceDaoMock.findByUuid(vmMockUuid)).thenReturn(vmInstanceMock); + when(_workJobDao.listPendingWorkJobs(any(), anyLong())).thenReturn(Collections.emptyList()); + when(_haMgr.hasPendingHaWork(anyLong())).thenReturn(false); + doReturn(guru).when(virtualMachineManagerImpl).getVmGuru(vmInstanceMock); + doNothing().when(virtualMachineManagerImpl).unmanageVMSnapshots(vmInstanceMock); + doNothing().when(virtualMachineManagerImpl).unmanageVMNics(any(VirtualMachineProfile.class), any(VMInstanceVO.class)); + doNothing().when(virtualMachineManagerImpl).unmanageVMVolumes(vmInstanceMock); + doNothing().when(guru).finalizeUnmanage(vmInstanceMock); + try (MockedStatic ignored = Mockito.mockStatic(ApiDBUtils.class)) { + when(ApiDBUtils.findHostById(hostMockId)).thenReturn(null); + Pair result = virtualMachineManagerImpl.unmanage(vmMockUuid, null); + assertNull(result.second()); + } + } + + @Test + public void testUnmanageSuccessNonKvm() { + when(vmInstanceMock.getHostId()).thenReturn(hostMockId); + when(hostMock.getUuid()).thenReturn(hostUuid); + when(vmInstanceDaoMock.findByUuid(vmMockUuid)).thenReturn(vmInstanceMock); + when(_workJobDao.listPendingWorkJobs(any(), anyLong())).thenReturn(Collections.emptyList()); + when(_haMgr.hasPendingHaWork(anyLong())).thenReturn(false); + doReturn(guru).when(virtualMachineManagerImpl).getVmGuru(vmInstanceMock); + doNothing().when(virtualMachineManagerImpl).unmanageVMSnapshots(vmInstanceMock); + doNothing().when(virtualMachineManagerImpl).unmanageVMNics(any(VirtualMachineProfile.class), any(VMInstanceVO.class)); + doNothing().when(virtualMachineManagerImpl).unmanageVMVolumes(vmInstanceMock); + doNothing().when(guru).finalizeUnmanage(vmInstanceMock); + try (MockedStatic ignored = Mockito.mockStatic(ApiDBUtils.class)) { + when(ApiDBUtils.findHostById(hostMockId)).thenReturn(hostMock); + try (MockedStatic actionUtil = Mockito.mockStatic(ActionEventUtils.class)) { + actionUtil.when(() -> ActionEventUtils.onActionEvent( + anyLong(), anyLong(), anyLong(), + anyString(), anyString(), + anyLong(), anyString() + )).thenReturn(1L); + + Pair result = virtualMachineManagerImpl.unmanage(vmMockUuid, null); + assertNotNull(result); + assertTrue(result.first()); + assertEquals(hostUuid, result.second()); + + verify(virtualMachineManagerImpl, never()).persistDomainForKVM(any(VMInstanceVO.class), anyLong()); + verify(virtualMachineManagerImpl, times(1)).unmanageVMSnapshots(vmInstanceMock); + verify(virtualMachineManagerImpl, times(1)).unmanageVMNics(any(VirtualMachineProfile.class), any(VMInstanceVO.class)); + verify(virtualMachineManagerImpl, times(1)).unmanageVMVolumes(vmInstanceMock); + verify(guru, times(1)).finalizeUnmanage(vmInstanceMock); + } + } + } + + @Test + public void testUnmanageSuccessKvm() throws Exception { + when(vmInstanceMock.getHostId()).thenReturn(hostMockId); + when(hostMock.getUuid()).thenReturn(hostUuid); + when(vmInstanceMock.getHypervisorType()).thenReturn(HypervisorType.KVM); + when(vmInstanceDaoMock.findByUuid(vmMockUuid)).thenReturn(vmInstanceMock); + when(_workJobDao.listPendingWorkJobs(any(), anyLong())).thenReturn(Collections.emptyList()); + when(_haMgr.hasPendingHaWork(anyLong())).thenReturn(false); + doReturn(guru).when(virtualMachineManagerImpl).getVmGuru(vmInstanceMock); + doNothing().when(virtualMachineManagerImpl).unmanageVMSnapshots(vmInstanceMock); + doNothing().when(virtualMachineManagerImpl).unmanageVMNics(any(VirtualMachineProfile.class), any(VMInstanceVO.class)); + doNothing().when(virtualMachineManagerImpl).unmanageVMVolumes(vmInstanceMock); + doNothing().when(guru).finalizeUnmanage(vmInstanceMock); + try (MockedStatic ignored = Mockito.mockStatic(ApiDBUtils.class)) { + when(ApiDBUtils.findHostById(hostMockId)).thenReturn(hostMock); + try (MockedStatic actionUtil = Mockito.mockStatic(ActionEventUtils.class)) { + actionUtil.when(() -> ActionEventUtils.onActionEvent( + anyLong(), anyLong(), anyLong(), + anyString(), anyString(), + anyLong(), anyString() + )).thenReturn(1L); + UnmanageInstanceAnswer successAnswer = new UnmanageInstanceAnswer(null, true, "success"); + when(agentManagerMock.send(anyLong(), any(UnmanageInstanceCommand.class))).thenReturn(successAnswer); + Pair result = virtualMachineManagerImpl.unmanage(vmMockUuid, null); + assertNotNull(result); + assertTrue(result.first()); + assertEquals(hostUuid, result.second()); + verify(virtualMachineManagerImpl, times(1)).persistDomainForKVM(vmInstanceMock, null); + verify(virtualMachineManagerImpl, times(1)).unmanageVMSnapshots(vmInstanceMock); + verify(virtualMachineManagerImpl, times(1)).unmanageVMNics(any(VirtualMachineProfile.class), any(VMInstanceVO.class)); + verify(virtualMachineManagerImpl, times(1)).unmanageVMVolumes(vmInstanceMock); + verify(guru, times(1)).finalizeUnmanage(vmInstanceMock); + } + } + } + + @Test + public void testUpdateClvmLockHostForVmVolumes_WithClvmVolumes() throws Exception { + long vmId = 100L; + long destHostId = 2L; + long poolId = 10L; + + VolumeVO clvmVolume1 = mock(VolumeVO.class); + VolumeVO clvmVolume2 = mock(VolumeVO.class); + + when(clvmVolume1.getId()).thenReturn(1L); + when(clvmVolume1.getPoolId()).thenReturn(poolId); + when(clvmVolume2.getId()).thenReturn(2L); + when(clvmVolume2.getPoolId()).thenReturn(poolId); + + StoragePoolVO clvmPool = mock(StoragePoolVO.class); + when(clvmPool.getPoolType()).thenReturn(Storage.StoragePoolType.CLVM); + + when(volumeDaoMock.findByInstance(vmId)).thenReturn(Arrays.asList(clvmVolume1, clvmVolume2)); + when(storagePoolDaoMock.findById(poolId)).thenReturn(clvmPool); + + ClvmPoolManager clvmPoolManagerMock = injectMockedClvmPoolManager(); + + Method method = getUpdateClvmLockHostForVmVolumesMethod(); + method.invoke(virtualMachineManagerImpl, vmId, destHostId); + + verify(clvmPoolManagerMock, times(1)).setClvmLockHostId(1L, destHostId); + verify(clvmPoolManagerMock, times(1)).setClvmLockHostId(2L, destHostId); + } + + @Test + public void testUpdateClvmLockHostForVmVolumes_WithNonClvmVolumes() throws Exception { + long vmId = 100L; + long destHostId = 2L; + long poolId = 10L; + + VolumeVO nfsVolume = mock(VolumeVO.class); + when(nfsVolume.getPoolId()).thenReturn(poolId); + + StoragePoolVO nfsPool = mock(StoragePoolVO.class); + when(nfsPool.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem); + + when(volumeDaoMock.findByInstance(vmId)).thenReturn(Arrays.asList(nfsVolume)); + when(storagePoolDaoMock.findById(poolId)).thenReturn(nfsPool); + + ClvmPoolManager clvmPoolManagerMock = injectMockedClvmPoolManager(); + + Method method = getUpdateClvmLockHostForVmVolumesMethod(); + method.invoke(virtualMachineManagerImpl, vmId, destHostId); + + verify(clvmPoolManagerMock, never()).setClvmLockHostId(anyLong(), anyLong()); + } + + @Test + public void testUpdateClvmLockHostForVmVolumes_WithMixedVolumes() throws Exception { + long vmId = 100L; + long destHostId = 2L; + long clvmPoolId = 10L; + long nfsPoolId = 20L; + + VolumeVO clvmVolume = mock(VolumeVO.class); + VolumeVO nfsVolume = mock(VolumeVO.class); + + when(clvmVolume.getId()).thenReturn(1L); + when(clvmVolume.getPoolId()).thenReturn(clvmPoolId); + when(nfsVolume.getPoolId()).thenReturn(nfsPoolId); + + StoragePoolVO clvmPool = mock(StoragePoolVO.class); + when(clvmPool.getPoolType()).thenReturn(Storage.StoragePoolType.CLVM); + + StoragePoolVO nfsPool = mock(StoragePoolVO.class); + when(nfsPool.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem); + + when(volumeDaoMock.findByInstance(vmId)).thenReturn(Arrays.asList(clvmVolume, nfsVolume)); + when(storagePoolDaoMock.findById(clvmPoolId)).thenReturn(clvmPool); + when(storagePoolDaoMock.findById(nfsPoolId)).thenReturn(nfsPool); + + ClvmPoolManager clvmPoolManagerMock = injectMockedClvmPoolManager(); + + Method method = getUpdateClvmLockHostForVmVolumesMethod(); + method.invoke(virtualMachineManagerImpl, vmId, destHostId); + + verify(clvmPoolManagerMock, times(1)).setClvmLockHostId(1L, destHostId); + verify(clvmPoolManagerMock, never()).setClvmLockHostId(2L, destHostId); + } + + @Test + public void testUpdateClvmLockHostForVmVolumes_WithNoVolumes() throws Exception { + long vmId = 100L; + long destHostId = 2L; + + when(volumeDaoMock.findByInstance(vmId)).thenReturn(Collections.emptyList()); + + ClvmPoolManager clvmPoolManagerMock = injectMockedClvmPoolManager(); + + Method method = getUpdateClvmLockHostForVmVolumesMethod(); + method.invoke(virtualMachineManagerImpl, vmId, destHostId); + + verify(clvmPoolManagerMock, never()).setClvmLockHostId(anyLong(), anyLong()); + } + + @Test + public void testUpdateClvmLockHostForVmVolumes_WithNullPoolId() throws Exception { + long vmId = 100L; + long destHostId = 2L; + + VolumeVO volumeWithoutPool = mock(VolumeVO.class); + when(volumeWithoutPool.getPoolId()).thenReturn(null); + + when(volumeDaoMock.findByInstance(vmId)).thenReturn(Arrays.asList(volumeWithoutPool)); + + ClvmPoolManager clvmPoolManagerMock = injectMockedClvmPoolManager(); + + Method method = getUpdateClvmLockHostForVmVolumesMethod(); + method.invoke(virtualMachineManagerImpl, vmId, destHostId); + + verify(storagePoolDaoMock, never()).findById(anyLong()); + verify(clvmPoolManagerMock, never()).setClvmLockHostId(anyLong(), anyLong()); + } + + @Test + public void testUpdateClvmLockHostForVmVolumes_WithNullPool() throws Exception { + long vmId = 100L; + long destHostId = 2L; + long poolId = 10L; + + VolumeVO volume = mock(VolumeVO.class); + when(volume.getPoolId()).thenReturn(poolId); + + when(volumeDaoMock.findByInstance(vmId)).thenReturn(Arrays.asList(volume)); + when(storagePoolDaoMock.findById(poolId)).thenReturn(null); + + ClvmPoolManager clvmPoolManagerMock = injectMockedClvmPoolManager(); + + Method method = getUpdateClvmLockHostForVmVolumesMethod(); + method.invoke(virtualMachineManagerImpl, vmId, destHostId); + + verify(clvmPoolManagerMock, never()).setClvmLockHostId(anyLong(), anyLong()); + } + + @Test + public void testUpdateClvmLockHostForVmVolumes_MultipleClvmPools() throws Exception { + long vmId = 100L; + long destHostId = 2L; + long pool1Id = 10L; + long pool2Id = 20L; + + VolumeVO volume1 = mock(VolumeVO.class); + VolumeVO volume2 = mock(VolumeVO.class); + VolumeVO volume3 = mock(VolumeVO.class); + + when(volume1.getId()).thenReturn(1L); + when(volume1.getPoolId()).thenReturn(pool1Id); + when(volume2.getId()).thenReturn(2L); + when(volume2.getPoolId()).thenReturn(pool2Id); + when(volume3.getId()).thenReturn(3L); + when(volume3.getPoolId()).thenReturn(pool1Id); + + StoragePoolVO clvmPool1 = mock(StoragePoolVO.class); + when(clvmPool1.getPoolType()).thenReturn(Storage.StoragePoolType.CLVM); + + StoragePoolVO clvmPool2 = mock(StoragePoolVO.class); + when(clvmPool2.getPoolType()).thenReturn(Storage.StoragePoolType.CLVM); + + when(volumeDaoMock.findByInstance(vmId)).thenReturn(Arrays.asList(volume1, volume2, volume3)); + when(storagePoolDaoMock.findById(pool1Id)).thenReturn(clvmPool1); + when(storagePoolDaoMock.findById(pool2Id)).thenReturn(clvmPool2); + + ClvmPoolManager clvmPoolManagerMock = injectMockedClvmPoolManager(); + + Method method = getUpdateClvmLockHostForVmVolumesMethod(); + method.invoke(virtualMachineManagerImpl, vmId, destHostId); + + verify(clvmPoolManagerMock, times(1)).setClvmLockHostId(1L, destHostId); + verify(clvmPoolManagerMock, times(1)).setClvmLockHostId(2L, destHostId); + verify(clvmPoolManagerMock, times(1)).setClvmLockHostId(3L, destHostId); + } + } 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 064ca44c69a2..66f5b699cc46 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 @@ -27,6 +27,7 @@ import java.net.URI; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -35,11 +36,15 @@ import com.cloud.exception.InsufficientVirtualNetworkCapacityException; import com.cloud.network.IpAddressManager; import com.cloud.utils.Pair; +import org.apache.cloudstack.extension.Extension; +import org.apache.cloudstack.extension.ExtensionHelper; +import org.apache.cloudstack.framework.extensions.network.NetworkExtensionElement; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import org.springframework.test.util.ReflectionTestUtils; import org.mockito.ArgumentMatchers; import org.mockito.MockedStatic; import org.mockito.Mockito; @@ -68,6 +73,7 @@ import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.network.dao.RouterNetworkDao; import com.cloud.network.element.DhcpServiceProvider; +import com.cloud.network.element.NetworkElement; import com.cloud.network.guru.GuestNetworkGuru; import com.cloud.network.guru.NetworkGuru; import com.cloud.network.vpc.VpcManager; @@ -105,6 +111,7 @@ public class NetworkOrchestratorTest extends TestCase { private String guruName = "GuestNetworkGuru"; private String dhcpProvider = "VirtualRouter"; private NetworkGuru guru = mock(NetworkGuru.class); + private NetworkExtensionElement networkExtensionElement; NetworkOfferingVO networkOffering = mock(NetworkOfferingVO.class); @@ -135,6 +142,9 @@ public void setUp() { testOrchestrator.routerJoinDao = mock(DomainRouterJoinDao.class); testOrchestrator._ipAddrMgr = mock(IpAddressManager.class); testOrchestrator._entityMgr = mock(EntityManager.class); + testOrchestrator.extensionHelper = mock(ExtensionHelper.class); + networkExtensionElement = mock(NetworkExtensionElement.class); + ReflectionTestUtils.setField(testOrchestrator, "networkExtensionElement", networkExtensionElement); DhcpServiceProvider provider = mock(DhcpServiceProvider.class); Map capabilities = new HashMap(); @@ -388,7 +398,7 @@ private void configureTestConfigureNicProfileBasedOnRequestedIpTests(NicProfile when(testOrchestrator._ipAddressDao.findByIpAndSourceNetworkId(Mockito.anyLong(), Mockito.anyString())).thenReturn(ipVoSpy); } - VlanVO vlanSpy = Mockito.spy(new VlanVO(Vlan.VlanType.DirectAttached, "vlanTag", vlanGateway, vlanNetmask, 0l, "192.168.100.100 - 192.168.100.200", 0l, new Long(0l), + VlanVO vlanSpy = Mockito.spy(new VlanVO(Vlan.VlanType.DirectAttached, "vlanTag", vlanGateway, vlanNetmask, 0l, "192.168.100.100 - 192.168.100.200", 0l, 0l, "ip6Gateway", "ip6Cidr", "ip6Range")); Mockito.doReturn(0l).when(vlanSpy).getId(); @@ -396,6 +406,7 @@ private void configureTestConfigureNicProfileBasedOnRequestedIpTests(NicProfile when(testOrchestrator._ipAddressDao.acquireInLockTable(Mockito.anyLong())).thenReturn(ipVoSpy); when(testOrchestrator._ipAddressDao.update(Mockito.anyLong(), Mockito.any(IPAddressVO.class))).thenReturn(true); when(testOrchestrator._ipAddressDao.releaseFromLockTable(Mockito.anyLong())).thenReturn(true); + when(testOrchestrator._networkModel.isMACUnique(Mockito.anyString(), Mockito.anyLong())).thenReturn(true); try { when(testOrchestrator._networkModel.getNextAvailableMacAddressInNetwork(Mockito.anyLong())).thenReturn(macAddress); } catch (InsufficientAddressCapacityException e) { @@ -822,7 +833,7 @@ public void testGetGuestIpForNicImportBasicZoneManualIP() { Mockito.when(network.getId()).thenReturn(networkId); Mockito.when(dataCenter.getId()).thenReturn(dataCenterId); Mockito.when(ipAddresses.getIp4Address()).thenReturn(requestedIp); - Mockito.when(testOrchestrator._ipAddressDao.findByIp(requestedIp)).thenReturn(ipAddressVO); + Mockito.when(testOrchestrator._ipAddressDao.findByIpAndSourceNetworkId(networkId, requestedIp)).thenReturn(ipAddressVO); String ipAddress = testOrchestrator.getSelectedIpForNicImport(network, dataCenter, ipAddresses); Assert.assertEquals(requestedIp, ipAddress); } @@ -1009,4 +1020,57 @@ public void testImportNicWithIP4Address() throws Exception { assertEquals("testtag", nicProfile.getName()); } } + + // ----------------------------------------------------------------------- + // Tests for getNetworkElementsIncludingExtensions + // ----------------------------------------------------------------------- + + @Test + public void getNetworkElementsIncludingExtensionsReturnsBaseListWhenNoExtensions() { + when(testOrchestrator.extensionHelper.listExtensionsByType(Extension.Type.NetworkOrchestrator)) + .thenReturn(Collections.emptyList()); + + DhcpServiceProvider dhcpProvider = mock(DhcpServiceProvider.class); + List elements = new ArrayList<>(List.of(dhcpProvider)); + testOrchestrator.networkElements = elements; + + List result = testOrchestrator.getNetworkElementsIncludingExtensions(); + + assertNotNull(result); + assertEquals(elements.size(), result.size()); + } + + @Test + public void getNetworkElementsIncludingExtensionsAddsExtensionElements() { + Extension ext = mock(Extension.class); + when(ext.getName()).thenReturn("my-net-ext"); + when(testOrchestrator.extensionHelper.listExtensionsByType(Extension.Type.NetworkOrchestrator)) + .thenReturn(List.of(ext)); + + NetworkExtensionElement extElement = mock(NetworkExtensionElement.class); + when(networkExtensionElement.withProviderName("my-net-ext")).thenReturn(extElement); + + DhcpServiceProvider dhcpProvider = mock(DhcpServiceProvider.class); + testOrchestrator.networkElements = new ArrayList<>(List.of(dhcpProvider)); + + List result = testOrchestrator.getNetworkElementsIncludingExtensions(); + + assertNotNull(result); + assertEquals(2, result.size()); + assertTrue(result.contains(extElement)); + } + + @Test + public void getNetworkElementsIncludingExtensionsReturnsBaseListWhenExtensionHelperReturnsNull() { + when(testOrchestrator.extensionHelper.listExtensionsByType(Extension.Type.NetworkOrchestrator)) + .thenReturn(null); + + DhcpServiceProvider dhcpProvider = mock(DhcpServiceProvider.class); + testOrchestrator.networkElements = new ArrayList<>(List.of(dhcpProvider)); + + List result = testOrchestrator.getNetworkElementsIncludingExtensions(); + + assertNotNull(result); + assertEquals(1, result.size()); + } } 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 817ff02ef745..d61caf16ae5d 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 @@ -16,6 +16,8 @@ // under the License. package org.apache.cloudstack.engine.orchestration; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -30,6 +32,7 @@ import com.cloud.host.HostVO; import com.cloud.hypervisor.Hypervisor; import com.cloud.offering.DiskOffering; +import com.cloud.storage.clvm.ClvmPoolManager; import com.cloud.storage.ScopeType; import com.cloud.storage.DataStoreRole; import com.cloud.storage.Storage; @@ -42,6 +45,7 @@ import com.cloud.uservm.UserVm; import com.cloud.utils.db.EntityManager; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.utils.Pair; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; @@ -67,6 +71,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockedConstruction; @@ -241,7 +246,7 @@ public void testImportVolume() { volumeOrchestrator.importVolume(volumeType, name, diskOffering, sizeInBytes, null, null, zoneId, hypervisorType, null, null, owner, - deviceId, poolId, path, chainInfo); + deviceId, poolId, Storage.StoragePoolType.NetworkFilesystem, path, chainInfo); VolumeVO volume = volumeVOMockedConstructionConstruction.constructed().get(0); Mockito.verify(volume, Mockito.never()).setInstanceId(Mockito.anyLong()); @@ -275,6 +280,7 @@ public void testAllocateDuplicateVolumeVOBasic() { Mockito.when(oldVol.isRecreatable()).thenReturn(false); Mockito.when(oldVol.getFormat()).thenReturn(Storage.ImageFormat.QCOW2); Mockito.when(oldVol.getPassphraseId()).thenReturn(null); // no encryption + Mockito.when(oldVol.getKmsKeyId()).thenReturn(null); // no encryption VolumeVO persistedVol = Mockito.mock(VolumeVO.class); Mockito.when(volumeDao.persist(Mockito.any(VolumeVO.class))).thenReturn(persistedVol); @@ -303,6 +309,7 @@ public void testAllocateDuplicateVolumeVOWithEncryption() { Mockito.when(oldVol.getInstanceId()).thenReturn(7L); Mockito.when(oldVol.isRecreatable()).thenReturn(true); Mockito.when(oldVol.getFormat()).thenReturn(Storage.ImageFormat.RAW); + Mockito.when(oldVol.getKmsKeyId()).thenReturn(null); Mockito.when(oldVol.getPassphraseId()).thenReturn(42L); PassphraseVO passphrase = Mockito.mock(PassphraseVO.class); @@ -336,9 +343,6 @@ public void testAllocateDuplicateVolumeVOWithTemplateOverride() { 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); } @@ -640,4 +644,305 @@ public void getVolumeCheckpointPathsAndImageStoreUrlsTestReturnCheckpointIfKVMAn Assert.assertEquals(1, result.second().size()); } + @Test + public void testTransferClvmLocksForVmStart_WithClvmVolumes() throws Exception { + Long destHostId = 2L; + Long currentHostId = 1L; + Long poolId = 10L; + + VolumeVO clvmVolume1 = Mockito.mock(VolumeVO.class); + VolumeVO clvmVolume2 = Mockito.mock(VolumeVO.class); + + Mockito.when(clvmVolume1.getId()).thenReturn(101L); + Mockito.when(clvmVolume1.getPoolId()).thenReturn(poolId); + Mockito.when(clvmVolume1.getUuid()).thenReturn("vol-uuid-1"); + Mockito.when(clvmVolume1.getPath()).thenReturn("vol-path-1"); + + Mockito.when(clvmVolume2.getId()).thenReturn(102L); + Mockito.when(clvmVolume2.getPoolId()).thenReturn(poolId); + Mockito.when(clvmVolume2.getUuid()).thenReturn("vol-uuid-2"); + Mockito.when(clvmVolume2.getPath()).thenReturn("vol-path-2"); + + StoragePoolVO clvmPool = Mockito.mock(StoragePoolVO.class); + Mockito.when(clvmPool.getPoolType()).thenReturn(Storage.StoragePoolType.CLVM); + + VMInstanceVO vmInstance = Mockito.mock(VMInstanceVO.class); + Mockito.when(vmInstance.getInstanceName()).thenReturn(MOCK_VM_NAME); + + ClvmPoolManager clvmPoolManager = Mockito.mock(ClvmPoolManager.class); + Mockito.when(clvmPoolManager.getClvmLockHostId(Mockito.eq(101L), Mockito.anyString(), + Mockito.anyString(), Mockito.any(), Mockito.eq(true))).thenReturn(currentHostId); + Mockito.when(clvmPoolManager.getClvmLockHostId(Mockito.eq(102L), Mockito.anyString(), + Mockito.anyString(), Mockito.any(), Mockito.eq(true))).thenReturn(currentHostId); + Mockito.when(clvmPoolManager.transferClvmVolumeLock(Mockito.anyString(), Mockito.anyLong(), + Mockito.anyString(), Mockito.any(), Mockito.anyLong(), Mockito.anyLong())).thenReturn(true); + + Mockito.when(storagePoolDao.findById(poolId)).thenReturn(clvmPool); + + setField(volumeOrchestrator, "clvmPoolManager", clvmPoolManager); + setField(volumeOrchestrator, "_storagePoolDao", storagePoolDao); + + Method method = VolumeOrchestrator.class.getDeclaredMethod( + "transferClvmLocksForVmStart", List.class, Long.class, VMInstanceVO.class); + method.setAccessible(true); + + method.invoke(volumeOrchestrator, List.of(clvmVolume1, clvmVolume2), destHostId, vmInstance); + + Mockito.verify(clvmPoolManager, Mockito.times(1)).transferClvmVolumeLock( + Mockito.eq("vol-uuid-1"), Mockito.eq(101L), Mockito.eq("vol-path-1"), + Mockito.eq(clvmPool), Mockito.eq(currentHostId), Mockito.eq(destHostId)); + Mockito.verify(clvmPoolManager, Mockito.times(1)).transferClvmVolumeLock( + Mockito.eq("vol-uuid-2"), Mockito.eq(102L), Mockito.eq("vol-path-2"), + Mockito.eq(clvmPool), Mockito.eq(currentHostId), Mockito.eq(destHostId)); + } + + @Test + public void testTransferClvmLocksForVmStart_WithNonClvmVolumes() throws Exception { + Long destHostId = 2L; + Long poolId = 10L; + + VolumeVO nfsVolume = Mockito.mock(VolumeVO.class); + Mockito.when(nfsVolume.getPoolId()).thenReturn(poolId); + + StoragePoolVO nfsPool = Mockito.mock(StoragePoolVO.class); + Mockito.when(nfsPool.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem); + + VMInstanceVO vmInstance = Mockito.mock(VMInstanceVO.class); + + ClvmPoolManager clvmPoolManager = Mockito.mock(ClvmPoolManager.class); + + Mockito.when(storagePoolDao.findById(poolId)).thenReturn(nfsPool); + + setField(volumeOrchestrator, "clvmPoolManager", clvmPoolManager); + setField(volumeOrchestrator, "_storagePoolDao", storagePoolDao); + + Method method = VolumeOrchestrator.class.getDeclaredMethod( + "transferClvmLocksForVmStart", List.class, Long.class, VMInstanceVO.class); + method.setAccessible(true); + + method.invoke(volumeOrchestrator, List.of(nfsVolume), destHostId, vmInstance); + + Mockito.verify(clvmPoolManager, Mockito.never()).transferClvmVolumeLock( + Mockito.anyString(), Mockito.anyLong(), Mockito.anyString(), + Mockito.any(), Mockito.anyLong(), Mockito.anyLong()); + } + + @Test + public void testTransferClvmLocksForVmStart_NoLockTransferNeeded() throws Exception { + Long destHostId = 2L; + Long poolId = 10L; + + VolumeVO clvmVolume = Mockito.mock(VolumeVO.class); + Mockito.when(clvmVolume.getId()).thenReturn(101L); + Mockito.when(clvmVolume.getPoolId()).thenReturn(poolId); + + StoragePoolVO clvmPool = Mockito.mock(StoragePoolVO.class); + Mockito.when(clvmPool.getPoolType()).thenReturn(Storage.StoragePoolType.CLVM); + + VMInstanceVO vmInstance = Mockito.mock(VMInstanceVO.class); + + ClvmPoolManager clvmPoolManager = Mockito.mock(ClvmPoolManager.class); + Mockito.when(clvmPoolManager.getClvmLockHostId(Mockito.eq(101L), ArgumentMatchers.nullable(String.class), + ArgumentMatchers.nullable(String.class), Mockito.any(), Mockito.eq(true))).thenReturn(destHostId); + + Mockito.when(storagePoolDao.findById(poolId)).thenReturn(clvmPool); + + setField(volumeOrchestrator, "clvmPoolManager", clvmPoolManager); + setField(volumeOrchestrator, "_storagePoolDao", storagePoolDao); + + java.lang.reflect.Method method = VolumeOrchestrator.class.getDeclaredMethod( + "transferClvmLocksForVmStart", List.class, Long.class, VMInstanceVO.class); + method.setAccessible(true); + + method.invoke(volumeOrchestrator, List.of(clvmVolume), destHostId, vmInstance); + + Mockito.verify(clvmPoolManager, Mockito.never()).transferClvmVolumeLock( + Mockito.anyString(), Mockito.anyLong(), Mockito.anyString(), + Mockito.any(), Mockito.anyLong(), Mockito.anyLong()); + } + + @Test + public void testTransferClvmLocksForVmStart_EmptyVolumeList() throws Exception { + Long destHostId = 2L; + VMInstanceVO vmInstance = Mockito.mock(VMInstanceVO.class); + + ClvmPoolManager clvmPoolManager = Mockito.mock(ClvmPoolManager.class); + setField(volumeOrchestrator, "clvmPoolManager", clvmPoolManager); + + Method method = VolumeOrchestrator.class.getDeclaredMethod( + "transferClvmLocksForVmStart", List.class, Long.class, VMInstanceVO.class); + method.setAccessible(true); + + method.invoke(volumeOrchestrator, new ArrayList(), destHostId, vmInstance); + + Mockito.verify(clvmPoolManager, Mockito.never()).getClvmLockHostId(Mockito.anyLong(), Mockito.anyString(), + Mockito.anyString(), Mockito.any(), Mockito.anyBoolean()); + } + + @Test + public void testTransferClvmLocksForVmStart_NullPoolId() throws Exception { + Long destHostId = 2L; + + VolumeVO volumeWithoutPool = Mockito.mock(VolumeVO.class); + Mockito.when(volumeWithoutPool.getPoolId()).thenReturn(null); + + VMInstanceVO vmInstance = Mockito.mock(VMInstanceVO.class); + + ClvmPoolManager clvmPoolManager = Mockito.mock(ClvmPoolManager.class); + setField(volumeOrchestrator, "clvmPoolManager", clvmPoolManager); + setField(volumeOrchestrator, "_storagePoolDao", storagePoolDao); + + Method method = VolumeOrchestrator.class.getDeclaredMethod( + "transferClvmLocksForVmStart", List.class, Long.class, VMInstanceVO.class); + method.setAccessible(true); + + method.invoke(volumeOrchestrator, List.of(volumeWithoutPool), destHostId, vmInstance); + + Mockito.verify(storagePoolDao, Mockito.never()).findById(Mockito.anyLong()); + } + + @Test + public void testTransferClvmLocksForVmStart_SetInitialLockHost() throws Exception { + Long destHostId = 2L; + Long poolId = 10L; + + VolumeVO clvmVolume = Mockito.mock(VolumeVO.class); + Mockito.when(clvmVolume.getId()).thenReturn(101L); + Mockito.when(clvmVolume.getPoolId()).thenReturn(poolId); + + StoragePoolVO clvmPool = Mockito.mock(StoragePoolVO.class); + Mockito.when(clvmPool.getPoolType()).thenReturn(Storage.StoragePoolType.CLVM); + + VMInstanceVO vmInstance = Mockito.mock(VMInstanceVO.class); + + ClvmPoolManager clvmPoolManager = Mockito.mock(ClvmPoolManager.class); + Mockito.when(clvmPoolManager.getClvmLockHostId(Mockito.eq(101L), ArgumentMatchers.nullable(String.class), + ArgumentMatchers.nullable(String.class), Mockito.any(), Mockito.eq(true))).thenReturn(null); + + Mockito.when(storagePoolDao.findById(poolId)).thenReturn(clvmPool); + + setField(volumeOrchestrator, "clvmPoolManager", clvmPoolManager); + setField(volumeOrchestrator, "_storagePoolDao", storagePoolDao); + + Method method = VolumeOrchestrator.class.getDeclaredMethod( + "transferClvmLocksForVmStart", List.class, Long.class, VMInstanceVO.class); + method.setAccessible(true); + + method.invoke(volumeOrchestrator, List.of(clvmVolume), destHostId, vmInstance); + + Mockito.verify(clvmPoolManager, Mockito.times(1)).setClvmLockHostId(101L, destHostId); + Mockito.verify(clvmPoolManager, Mockito.never()).transferClvmVolumeLock( + Mockito.anyString(), Mockito.anyLong(), Mockito.anyString(), + Mockito.any(), Mockito.anyLong(), Mockito.anyLong()); + } + + @Test + public void testTransferClvmLocksForVmStart_MixedVolumes() throws Exception { + Long destHostId = 2L; + Long currentHostId = 1L; + Long clvmPoolId = 10L; + Long nfsPoolId = 20L; + + VolumeVO clvmVolume = Mockito.mock(VolumeVO.class); + Mockito.when(clvmVolume.getId()).thenReturn(101L); + Mockito.when(clvmVolume.getPoolId()).thenReturn(clvmPoolId); + Mockito.when(clvmVolume.getUuid()).thenReturn("clvm-vol-uuid"); + Mockito.when(clvmVolume.getPath()).thenReturn("clvm-vol-path"); + + VolumeVO nfsVolume = Mockito.mock(VolumeVO.class); + Mockito.when(nfsVolume.getPoolId()).thenReturn(nfsPoolId); + + StoragePoolVO clvmPool = Mockito.mock(StoragePoolVO.class); + Mockito.when(clvmPool.getPoolType()).thenReturn(Storage.StoragePoolType.CLVM); + + StoragePoolVO nfsPool = Mockito.mock(StoragePoolVO.class); + Mockito.when(nfsPool.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem); + + VMInstanceVO vmInstance = Mockito.mock(VMInstanceVO.class); + Mockito.when(vmInstance.getInstanceName()).thenReturn(MOCK_VM_NAME); + + ClvmPoolManager clvmPoolManager = Mockito.mock(ClvmPoolManager.class); + Mockito.when(clvmPoolManager.getClvmLockHostId(Mockito.eq(101L), Mockito.anyString(), + Mockito.anyString(), Mockito.any(), Mockito.eq(true))).thenReturn(currentHostId); + Mockito.when(clvmPoolManager.transferClvmVolumeLock(Mockito.anyString(), Mockito.anyLong(), + Mockito.anyString(), Mockito.any(), Mockito.anyLong(), Mockito.anyLong())).thenReturn(true); + + Mockito.when(storagePoolDao.findById(clvmPoolId)).thenReturn(clvmPool); + Mockito.when(storagePoolDao.findById(nfsPoolId)).thenReturn(nfsPool); + + setField(volumeOrchestrator, "clvmPoolManager", clvmPoolManager); + setField(volumeOrchestrator, "_storagePoolDao", storagePoolDao); + + Method method = VolumeOrchestrator.class.getDeclaredMethod( + "transferClvmLocksForVmStart", List.class, Long.class, VMInstanceVO.class); + method.setAccessible(true); + + method.invoke(volumeOrchestrator, List.of(clvmVolume, nfsVolume), destHostId, vmInstance); + + Mockito.verify(clvmPoolManager, Mockito.times(1)).transferClvmVolumeLock( + Mockito.eq("clvm-vol-uuid"), Mockito.eq(101L), Mockito.eq("clvm-vol-path"), + Mockito.eq(clvmPool), Mockito.eq(currentHostId), Mockito.eq(destHostId)); + } + + @Test(expected = CloudRuntimeException.class) + public void testTransferClvmLocksForVmStart_TransferFails() throws Throwable { + Long destHostId = 2L; + Long currentHostId = 1L; + Long poolId = 10L; + + VolumeVO clvmVolume = Mockito.mock(VolumeVO.class); + Mockito.when(clvmVolume.getId()).thenReturn(101L); + Mockito.when(clvmVolume.getPoolId()).thenReturn(poolId); + Mockito.when(clvmVolume.getUuid()).thenReturn("vol-uuid"); + Mockito.when(clvmVolume.getPath()).thenReturn("vol-path"); + + StoragePoolVO clvmPool = Mockito.mock(StoragePoolVO.class); + Mockito.when(clvmPool.getPoolType()).thenReturn(Storage.StoragePoolType.CLVM); + + VMInstanceVO vmInstance = Mockito.mock(VMInstanceVO.class); + Mockito.when(vmInstance.getInstanceName()).thenReturn(MOCK_VM_NAME); + + ClvmPoolManager clvmPoolManager = Mockito.mock(ClvmPoolManager.class); + Mockito.when(clvmPoolManager.getClvmLockHostId(Mockito.eq(101L), Mockito.anyString(), + Mockito.anyString(), Mockito.any(), Mockito.eq(true))).thenReturn(currentHostId); + Mockito.when(clvmPoolManager.transferClvmVolumeLock(Mockito.anyString(), Mockito.anyLong(), + Mockito.anyString(), Mockito.any(), Mockito.anyLong(), Mockito.anyLong())).thenReturn(false); + + Mockito.when(storagePoolDao.findById(poolId)).thenReturn(clvmPool); + + setField(volumeOrchestrator, "clvmPoolManager", clvmPoolManager); + setField(volumeOrchestrator, "_storagePoolDao", storagePoolDao); + + Method method = VolumeOrchestrator.class.getDeclaredMethod( + "transferClvmLocksForVmStart", List.class, Long.class, VMInstanceVO.class); + method.setAccessible(true); + + try { + method.invoke(volumeOrchestrator, List.of(clvmVolume), destHostId, vmInstance); + } catch (InvocationTargetException e) { + throw e.getCause(); + } + } + + private void setField(Object target, String fieldName, Object value) throws Exception { + Field field = findField(target.getClass(), fieldName); + if (field == null) { + throw new NoSuchFieldException("Field " + fieldName + " not found in " + target.getClass()); + } + field.setAccessible(true); + field.set(target, value); + } + + private Field findField(Class clazz, String fieldName) { + Class current = clazz; + while (current != null && current != Object.class) { + try { + return current.getDeclaredField(fieldName); + } catch (NoSuchFieldException e) { + current = current.getSuperclass(); + } + } + return null; + } + } diff --git a/engine/pom.xml b/engine/pom.xml index b771138dc8c6..2de84eab85af 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.22.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT ../pom.xml diff --git a/engine/schema/pom.xml b/engine/schema/pom.xml index 2f216884c8b9..664d4909e677 100644 --- a/engine/schema/pom.xml +++ b/engine/schema/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.22.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT ../pom.xml @@ -48,6 +48,11 @@ cloud-framework-db ${project.version}
+ + org.apache.cloudstack + cloud-framework-kms + ${project.version} + com.mysql mysql-connector-j diff --git a/engine/schema/src/main/java/com/cloud/alert/dao/AlertDaoImpl.java b/engine/schema/src/main/java/com/cloud/alert/dao/AlertDaoImpl.java index 94d01f472ba5..97b7c54f0844 100644 --- a/engine/schema/src/main/java/com/cloud/alert/dao/AlertDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/alert/dao/AlertDaoImpl.java @@ -20,6 +20,7 @@ import java.util.List; +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.alert.AlertVO; @@ -28,7 +29,7 @@ import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Op; -import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.db.UpdateBuilder; @Component public class AlertDaoImpl extends GenericDaoBase implements AlertDao { @@ -107,25 +108,20 @@ public boolean archiveAlert(List ids, String type, Date startDate, Date en } sc.setParameters("archived", false); - boolean result = true; - ; List alerts = listBy(sc); if (ids != null && alerts.size() < ids.size()) { - result = false; - return result; + return false; } - if (alerts != null && !alerts.isEmpty()) { - TransactionLegacy txn = TransactionLegacy.currentTxn(); - txn.start(); - for (AlertVO alert : alerts) { - alert = lockRow(alert.getId(), true); - alert.setArchived(true); - update(alert.getId(), alert); - txn.commit(); - } - txn.close(); + + if (CollectionUtils.isEmpty(alerts)) { + return true; } - return result; + + AlertVO alertForUpdate = createForUpdate(); + alertForUpdate.setArchived(true); + UpdateBuilder ub = getUpdateBuilder(alertForUpdate); + update(ub, sc, null); + return true; } @Override 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 91949bded504..f65c3cf188cd 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 @@ -213,6 +213,8 @@ public class CapacityDaoImpl extends GenericDaoBase implements private static final String LEFT_JOIN_VM_TEMPLATE = "LEFT JOIN vm_template ON vm_template.id = vi.vm_template_id "; + private static final String STORAGE_POOLS_WITH_CHILDREN = "SELECT DISTINCT parent FROM storage_pool WHERE parent != 0 AND removed IS NULL"; + public CapacityDaoImpl() { _hostIdTypeSearch = createSearchBuilder(); _hostIdTypeSearch.and("hostId", _hostIdTypeSearch.entity().getHostOrPoolId(), SearchCriteria.Op.EQ); @@ -379,6 +381,11 @@ public List listCapacitiesGroupedByLevelAndType(Integer capacity finalQuery.append(" AND capacity_type = ?"); resourceIdList.add(capacityType.longValue()); } + + // Exclude storage pools with children from capacity calculations to avoid double counting + finalQuery.append(" AND NOT (capacity.capacity_type = ").append(Capacity.CAPACITY_TYPE_STORAGE_ALLOCATED) + .append(" AND capacity.host_id IN (").append(STORAGE_POOLS_WITH_CHILDREN).append("))"); + if (CollectionUtils.isNotEmpty(hostIds)) { finalQuery.append(String.format(" AND capacity.host_id IN (%s)", StringUtils.join(hostIds, ","))); if (capacityType == null) { @@ -541,6 +548,10 @@ public List findFilteredCapacityBy(Integer capacityType, Long zo StringBuilder sql = new StringBuilder(LIST_CAPACITY_GROUP_BY_CAPACITY_PART1); List resourceIdList = new ArrayList(); + // Exclude storage pools with children from capacity calculations to avoid double counting + sql.append(" AND NOT (capacity.capacity_type = ").append(Capacity.CAPACITY_TYPE_STORAGE_ALLOCATED) + .append(" AND capacity.host_id IN (").append(STORAGE_POOLS_WITH_CHILDREN).append("))"); + if (zoneId != null) { sql.append(" AND capacity.data_center_id = ?"); resourceIdList.add(zoneId); diff --git a/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDaoImpl.java index 4c752ff9b4ff..a8888f98ad29 100644 --- a/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDaoImpl.java @@ -171,7 +171,7 @@ public Scope getScope() { @Override public String getConfigValue(long id, String key) { ClusterDetailsVO vo = findDetail(id, key); - return vo == null ? null : vo.getValue(); + return vo == null ? null : getActualValue(vo); } @Override diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/AccountVlanMapDao.java b/engine/schema/src/main/java/com/cloud/dc/dao/AccountVlanMapDao.java index 01afd0780f7e..e4047cf7973d 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/AccountVlanMapDao.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/AccountVlanMapDao.java @@ -27,6 +27,6 @@ public interface AccountVlanMapDao extends GenericDao { public List listAccountVlanMapsByVlan(long vlanDbId); - public AccountVlanMapVO findAccountVlanMap(long accountId, long vlanDbId); + public AccountVlanMapVO findAccountVlanMap(Long accountId, long vlanDbId); } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/AccountVlanMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/AccountVlanMapDaoImpl.java index 12114770f112..0844bb77caa2 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/AccountVlanMapDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/AccountVlanMapDaoImpl.java @@ -48,9 +48,9 @@ public List listAccountVlanMapsByVlan(long vlanDbId) { } @Override - public AccountVlanMapVO findAccountVlanMap(long accountId, long vlanDbId) { + public AccountVlanMapVO findAccountVlanMap(Long accountId, long vlanDbId) { SearchCriteria sc = AccountVlanSearch.create(); - sc.setParameters("accountId", accountId); + sc.setParametersIfNotNull("accountId", accountId); sc.setParameters("vlanDbId", vlanDbId); return findOneIncludingRemovedBy(sc); } 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 6cfd2608f5de..3d04f6679def 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 @@ -23,6 +23,7 @@ import com.cloud.dc.ClusterVO; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDao; public interface ClusterDao extends GenericDao { @@ -61,4 +62,6 @@ public interface ClusterDao extends GenericDao { List listDistinctStorageAccessGroups(String name, String keyword); List listEnabledClusterIdsByZoneHypervisorArch(Long zoneId, HypervisorType hypervisorType, CPU.CPUArch arch); + + List listByZonesAndHypervisorType(List zoneIds, HypervisorType hypervisorType, Filter filter); } 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 c63af0a237ba..9b6f0f7ac3a6 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 @@ -20,6 +20,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -27,6 +28,7 @@ import javax.inject.Inject; +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.cpu.CPU; @@ -38,6 +40,7 @@ import com.cloud.org.Grouping; import com.cloud.org.Managed; import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.JoinBuilder; @@ -413,4 +416,19 @@ public List listEnabledClusterIdsByZoneHypervisorArch(Long zoneId, Hypervi } return customSearch(sc, null); } + + @Override + public List listByZonesAndHypervisorType(List zoneIds, HypervisorType hypervisorType, Filter filter) { + if (CollectionUtils.isEmpty(zoneIds)) { + return Collections.emptyList(); + } + SearchBuilder sb = createSearchBuilder(); + sb.and("dataCenterId", sb.entity().getDataCenterId(), SearchCriteria.Op.IN); + sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("dataCenterId", zoneIds.toArray()); + sc.setParameters("hypervisorType", hypervisorType.toString()); + return listBy(sc, filter); + } } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/ClusterVSMMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/ClusterVSMMapDaoImpl.java index 02a7ac6977c2..76058d213338 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/ClusterVSMMapDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/ClusterVSMMapDaoImpl.java @@ -36,7 +36,6 @@ public class ClusterVSMMapDaoImpl extends GenericDaoBase final SearchBuilder VsmSearch; public ClusterVSMMapDaoImpl() { - //super(); ClusterSearch = createSearchBuilder(); ClusterSearch.and("clusterId", ClusterSearch.entity().getClusterId(), SearchCriteria.Op.EQ); @@ -82,8 +81,6 @@ public boolean remove(Long id) { TransactionLegacy txn = TransactionLegacy.currentTxn(); txn.start(); ClusterVSMMapVO cluster = createForUpdate(); - //cluster.setClusterId(null); - //cluster.setVsmId(null); update(id, cluster); diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDetailsDaoImpl.java index bb03a96d02ee..687071699920 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDetailsDaoImpl.java @@ -31,7 +31,8 @@ public class DataCenterDetailsDaoImpl extends ResourceDetailsDaoBase DetailSearch; - DataCenterDetailsDaoImpl() { + public DataCenterDetailsDaoImpl() { + super(); DetailSearch = createSearchBuilder(); DetailSearch.and("zoneId", DetailSearch.entity().getResourceId(), SearchCriteria.Op.EQ); DetailSearch.and("name", DetailSearch.entity().getName(), SearchCriteria.Op.EQ); @@ -46,7 +47,7 @@ public Scope getScope() { @Override public String getConfigValue(long id, String key) { ResourceDetail vo = findDetail(id, key); - return vo == null ? null : vo.getValue(); + return vo == null ? null : getActualValue(vo); } @Override diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterIpAddressDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterIpAddressDaoImpl.java index ba01e31f80a8..6c1d944ca3e7 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterIpAddressDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterIpAddressDaoImpl.java @@ -161,14 +161,14 @@ public void addIpRange(long dcId, long podId, String start, String end, boolean } txn.commit(); } catch (SQLException ex) { - throw new CloudRuntimeException("Unable to persist ip address range ", ex); + throw new CloudRuntimeException("Unable to persist IP address range ", ex); } } @Override public void releaseIpAddress(String ipAddress, long dcId, Long nicId) { if (logger.isDebugEnabled()) { - logger.debug("Releasing ip address: " + ipAddress + " data center " + dcId); + logger.debug("Releasing IP address: " + ipAddress + " data center " + dcId); } SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("ip", ipAddress); @@ -186,7 +186,7 @@ public void releaseIpAddress(String ipAddress, long dcId, Long nicId) { @Override public void releaseIpAddress(long nicId, String reservationId) { if (logger.isDebugEnabled()) { - logger.debug("Releasing ip address for reservationId=" + reservationId + ", nic=" + nicId); + logger.debug("Releasing IP address for reservationId=" + reservationId + ", nic=" + nicId); } SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("nic", nicId); @@ -202,7 +202,7 @@ public void releaseIpAddress(long nicId, String reservationId) { @Override public void releasePodIpAddress(long id) { if (logger.isDebugEnabled()) { - logger.debug("Releasing ip address for ID=" + id); + logger.debug("Releasing IP address for ID=" + id); } DataCenterIpAddressVO vo = this.findById(id); @@ -215,7 +215,7 @@ public void releasePodIpAddress(long id) { @Override public void releaseIpAddress(long nicId) { if (logger.isDebugEnabled()) { - logger.debug("Releasing ip address for nic=" + nicId); + logger.debug("Releasing IP address for nic=" + nicId); } SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("nic", nicId); diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterLinkLocalIpAddressDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterLinkLocalIpAddressDaoImpl.java index 517f02edde78..1c9d8b439cbd 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterLinkLocalIpAddressDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterLinkLocalIpAddressDaoImpl.java @@ -104,7 +104,7 @@ public void addIpRange(long dcId, long podId, String start, String end) { @Override public void releaseIpAddress(String ipAddress, long dcId, long instanceId) { if (logger.isDebugEnabled()) { - logger.debug("Releasing ip address: " + ipAddress + " data center " + dcId); + logger.debug("Releasing IP address: " + ipAddress + " data center " + dcId); } SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("ip", ipAddress); diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/DomainVlanMapDao.java b/engine/schema/src/main/java/com/cloud/dc/dao/DomainVlanMapDao.java index 6af16bbace99..d14ccbe86ca6 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/DomainVlanMapDao.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/DomainVlanMapDao.java @@ -24,5 +24,5 @@ public interface DomainVlanMapDao extends GenericDao { public List listDomainVlanMapsByDomain(long domainId); public List listDomainVlanMapsByVlan(long vlanDbId); - public DomainVlanMapVO findDomainVlanMap(long domainId, long vlanDbId); + public DomainVlanMapVO findDomainVlanMap(Long domainId, long vlanDbId); } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/DomainVlanMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/DomainVlanMapDaoImpl.java index f789721d5fd6..0b4c781349fd 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/DomainVlanMapDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/DomainVlanMapDaoImpl.java @@ -46,9 +46,9 @@ public List listDomainVlanMapsByVlan(long vlanDbId) { } @Override - public DomainVlanMapVO findDomainVlanMap(long domainId, long vlanDbId) { + public DomainVlanMapVO findDomainVlanMap(Long domainId, long vlanDbId) { SearchCriteria sc = DomainVlanSearch.create(); - sc.setParameters("domainId", domainId); + sc.setParametersIfNotNull("domainId", domainId); sc.setParameters("vlanDbId", vlanDbId); return findOneIncludingRemovedBy(sc); } 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 a6c267bb189b..beab7ce2cd56 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 @@ -51,6 +51,8 @@ public interface VlanDao extends GenericDao { List listVlansByNetworkId(long networkId); + List listVlansByNetworkIds(List networkIds); + List listVlansByNetworkIdIncludingRemoved(long networkId); List listVlansByPhysicalNetworkId(long physicalNetworkId); 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 d9fad3cad12a..5687e8588772 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 @@ -20,6 +20,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -27,6 +28,7 @@ import javax.naming.ConfigurationException; import com.cloud.dc.VlanDetailsVO; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; @@ -134,7 +136,7 @@ public VlanDaoImpl() { ZoneTypeSearch.done(); NetworkVlanSearch = createSearchBuilder(); - NetworkVlanSearch.and("networkId", NetworkVlanSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); + NetworkVlanSearch.and("networkId", NetworkVlanSearch.entity().getNetworkId(), SearchCriteria.Op.IN); NetworkVlanSearch.done(); PhysicalNetworkVlanSearch = createSearchBuilder(); @@ -392,6 +394,16 @@ public List listVlansByNetworkId(long networkId) { return listBy(sc); } + @Override + public List listVlansByNetworkIds(List networkIds) { + if (CollectionUtils.isEmpty(networkIds)) { + return Collections.emptyList(); + } + SearchCriteria sc = NetworkVlanSearch.create(); + sc.setParameters("networkId", networkIds.toArray()); + return listBy(sc); + } + @Override public List listVlansByNetworkIdIncludingRemoved(long networkId) { SearchCriteria sc = NetworkVlanSearch.create(); sc.setParameters("networkId", networkId); diff --git a/engine/schema/src/main/java/com/cloud/domain/dao/DomainDaoImpl.java b/engine/schema/src/main/java/com/cloud/domain/dao/DomainDaoImpl.java index 56d971bbe015..1afa0d22dcc1 100644 --- a/engine/schema/src/main/java/com/cloud/domain/dao/DomainDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/domain/dao/DomainDaoImpl.java @@ -262,7 +262,7 @@ public boolean isChildDomain(Long parentId, Long childId) { SearchCriteria sc = DomainPairSearch.create(); sc.setParameters("id", parentId, childId); - List domainPair = listBy(sc); + List domainPair = listIncludingRemovedBy(sc); if ((domainPair != null) && (domainPair.size() == 2)) { DomainVO d1 = domainPair.get(0); diff --git a/engine/schema/src/main/java/com/cloud/event/UsageEventVO.java b/engine/schema/src/main/java/com/cloud/event/UsageEventVO.java index 3fc9fda94873..41ecec0c7fb8 100644 --- a/engine/schema/src/main/java/com/cloud/event/UsageEventVO.java +++ b/engine/schema/src/main/java/com/cloud/event/UsageEventVO.java @@ -75,6 +75,9 @@ public enum DynamicParameters { @Column(name = "virtual_size") private Long virtualSize; + @Column(name = "vm_id") + private Long vmId; + public UsageEventVO() { } @@ -143,6 +146,18 @@ public UsageEventVO(String usageType, long accountId, long zoneId, long vmId, lo this.offeringId = securityGroupId; } + public UsageEventVO(String usageType, long accountId, long zoneId, long resourceId, Long offeringId, Long templateId, Long size, Long vmId, String resourceName) { + this.type = usageType; + this.accountId = accountId; + this.zoneId = zoneId; + this.resourceId = resourceId; + this.offeringId = offeringId; + this.templateId = templateId; + this.size = size; + this.vmId = vmId; + this.resourceName = resourceName; + } + @Override public long getId() { return id; @@ -248,4 +263,11 @@ public void setVirtualSize(Long virtualSize) { this.virtualSize = virtualSize; } + public Long getVmId() { + return vmId; + } + + public void setVmId(Long vmId) { + this.vmId = vmId; + } } diff --git a/engine/schema/src/main/java/com/cloud/event/dao/EventDaoImpl.java b/engine/schema/src/main/java/com/cloud/event/dao/EventDaoImpl.java index e748e98900eb..9417ddd12595 100644 --- a/engine/schema/src/main/java/com/cloud/event/dao/EventDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/event/dao/EventDaoImpl.java @@ -18,8 +18,10 @@ import java.util.Date; import java.util.List; +import java.util.stream.Collectors; +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.event.Event.State; @@ -29,12 +31,13 @@ import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Op; -import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.db.UpdateBuilder; @Component public class EventDaoImpl extends GenericDaoBase implements EventDao { protected final SearchBuilder CompletedEventSearch; protected final SearchBuilder ToArchiveOrDeleteEventSearch; + protected final SearchBuilder ArchiveByIdsSearch; public EventDaoImpl() { CompletedEventSearch = createSearchBuilder(); @@ -51,6 +54,10 @@ public EventDaoImpl() { ToArchiveOrDeleteEventSearch.and("createdDateL", ToArchiveOrDeleteEventSearch.entity().getCreateDate(), Op.LTEQ); ToArchiveOrDeleteEventSearch.and("archived", ToArchiveOrDeleteEventSearch.entity().getArchived(), Op.EQ); ToArchiveOrDeleteEventSearch.done(); + + ArchiveByIdsSearch = createSearchBuilder(); + ArchiveByIdsSearch.and("id", ArchiveByIdsSearch.entity().getId(), Op.IN); + ArchiveByIdsSearch.done(); } @Override @@ -100,16 +107,16 @@ public List listToArchiveOrDeleteEvents(List ids, String type, Da @Override public void archiveEvents(List events) { - if (events != null && !events.isEmpty()) { - TransactionLegacy txn = TransactionLegacy.currentTxn(); - txn.start(); - for (EventVO event : events) { - event = lockRow(event.getId(), true); - event.setArchived(true); - update(event.getId(), event); - txn.commit(); - } - txn.close(); + if (CollectionUtils.isEmpty(events)) { + return; } + + List ids = events.stream().map(EventVO::getId).collect(Collectors.toList()); + SearchCriteria sc = ArchiveByIdsSearch.create(); + sc.setParameters("id", ids.toArray(new Object[ids.size()])); + EventVO eventForUpdate = createForUpdate(); + eventForUpdate.setArchived(true); + UpdateBuilder ub = getUpdateBuilder(eventForUpdate); + update(ub, sc, null); } } diff --git a/engine/schema/src/main/java/com/cloud/event/dao/UsageEventDaoImpl.java b/engine/schema/src/main/java/com/cloud/event/dao/UsageEventDaoImpl.java index fdef509da5bd..bce9c474e2d2 100644 --- a/engine/schema/src/main/java/com/cloud/event/dao/UsageEventDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/event/dao/UsageEventDaoImpl.java @@ -45,11 +45,11 @@ public class UsageEventDaoImpl extends GenericDaoBase implem private final SearchBuilder latestEventsSearch; private final SearchBuilder IpeventsSearch; private static final String COPY_EVENTS = - "INSERT INTO cloud_usage.usage_event (id, type, account_id, created, zone_id, resource_id, resource_name, offering_id, template_id, size, resource_type, virtual_size) " - + "SELECT id, type, account_id, created, zone_id, resource_id, resource_name, offering_id, template_id, size, resource_type, virtual_size FROM cloud.usage_event vmevt WHERE vmevt.id > ? and vmevt.id <= ? "; + "INSERT INTO cloud_usage.usage_event (id, type, account_id, created, zone_id, resource_id, resource_name, offering_id, template_id, size, resource_type, virtual_size, vm_id) " + + "SELECT id, type, account_id, created, zone_id, resource_id, resource_name, offering_id, template_id, size, resource_type, virtual_size, vm_id FROM cloud.usage_event vmevt WHERE vmevt.id > ? and vmevt.id <= ? "; private static final String COPY_ALL_EVENTS = - "INSERT INTO cloud_usage.usage_event (id, type, account_id, created, zone_id, resource_id, resource_name, offering_id, template_id, size, resource_type, virtual_size) " - + "SELECT id, type, account_id, created, zone_id, resource_id, resource_name, offering_id, template_id, size, resource_type, virtual_size FROM cloud.usage_event vmevt WHERE vmevt.id <= ?"; + "INSERT INTO cloud_usage.usage_event (id, type, account_id, created, zone_id, resource_id, resource_name, offering_id, template_id, size, resource_type, virtual_size, vm_id) " + + "SELECT id, type, account_id, created, zone_id, resource_id, resource_name, offering_id, template_id, size, resource_type, virtual_size, vm_id FROM cloud.usage_event vmevt WHERE vmevt.id <= ?"; private static final String COPY_EVENT_DETAILS = "INSERT INTO cloud_usage.usage_event_details (id, usage_event_id, name, value) " + "SELECT id, usage_event_id, name, value FROM cloud.usage_event_details vmevtDetails WHERE vmevtDetails.usage_event_id > ? and vmevtDetails.usage_event_id <= ? "; private static final String COPY_ALL_EVENT_DETAILS = "INSERT INTO cloud_usage.usage_event_details (id, usage_event_id, name, value) " 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 d51b4eca0577..468caf1e2271 100644 --- a/engine/schema/src/main/java/com/cloud/host/HostVO.java +++ b/engine/schema/src/main/java/com/cloud/host/HostVO.java @@ -45,7 +45,7 @@ import com.cloud.cpu.CPU; import org.apache.cloudstack.util.CPUArchConverter; import org.apache.cloudstack.util.HypervisorTypeConverter; -import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper; +import org.apache.cloudstack.utils.jsinterpreter.GenericRuleHelper; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.BooleanUtils; @@ -856,7 +856,8 @@ public boolean checkHostServiceOfferingTags(ServiceOffering serviceOffering) { } if (BooleanUtils.isTrue(this.getIsTagARule())) { - return TagAsRuleHelper.interpretTagAsRule(this.getHostTags().get(0), serviceOffering.getHostTag(), HostTagsDao.hostTagRuleExecutionTimeout.value()); + return GenericRuleHelper.interpretTagAsRule(this.getHostTags().get(0), serviceOffering.getHostTag(), HostTagsDao.hostTagRuleExecutionTimeout.value(), + HostTagsDao.hostTagRuleExecutionTimeout.key()); } if (StringUtils.isEmpty(serviceOffering.getHostTag())) { 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 090b019334f4..d8bdabc3dcbb 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 @@ -32,12 +32,17 @@ import com.cloud.utils.db.GenericDao; import com.cloud.utils.fsm.StateDao; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; +import org.apache.cloudstack.framework.config.ConfigKey; /** * Data Access Object for server * */ public interface HostDao extends GenericDao, StateDao { + + ConfigKey guestOsRuleExecutionTimeout = new ConfigKey<>("Advanced", Long.class, "guest.os.rule.execution.timeout", "3000", "The maximum runtime, in milliseconds, " + + "to execute a guest OS rule; if it is reached, a timeout will happen.", true); + long countBy(long clusterId, ResourceState... states); Integer countAllByType(final Host.Type type); @@ -116,6 +121,8 @@ public interface HostDao extends GenericDao, StateDao listIdsForUpEnabledByZoneAndHypervisor(Long zoneId, HypervisorType hypervisorType); + List findRoutingByClusterId(Long clusterId); + List findByClusterIdAndEncryptionSupport(Long clusterId); /** @@ -134,6 +141,8 @@ public interface HostDao extends GenericDao, StateDao listAllHostsByZoneAndHypervisorType(long zoneId, HypervisorType hypervisorType); + List listAllRoutingHostsByZoneAndHypervisorType(long zoneId, HypervisorType hypervisorType); + List listAllHostsThatHaveNoRuleTag(Host.Type type, Long clusterId, Long podId, Long dcId); HostVO findByPublicIp(String publicIp); @@ -218,7 +227,9 @@ int countHostsByMsResourceStateTypeAndHypervisorType(long msId, List listOrderedHostsHypervisorVersionsInDatacenter(long datacenterId, HypervisorType hypervisorType); - List findHostsWithTagRuleThatMatchComputeOferringTags(String computeOfferingTags); + List findHostsWithTagRuleThatMatchComputeOfferingTags(String computeOfferingTags); + + List findHostsWithGuestOsRulesThatDidNotMatchOsOfGuestVm(String templateGuestOSName); List findClustersThatMatchHostTagRule(String computeOfferingTags); 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 8f218841b074..15727d9d8e66 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 @@ -37,7 +37,9 @@ import com.cloud.vm.VirtualMachine; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; -import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; +import org.apache.cloudstack.utils.jsinterpreter.GenericRuleHelper; import org.apache.commons.collections.CollectionUtils; import com.cloud.agent.api.VgpuTypesInfo; @@ -84,7 +86,7 @@ @DB @TableGenerator(name = "host_req_sq", table = "op_host", pkColumnName = "id", valueColumnName = "sequence", allocationSize = 1) -public class HostDaoImpl extends GenericDaoBase implements HostDao { //FIXME: , ExternalIdDao { +public class HostDaoImpl extends GenericDaoBase implements HostDao, Configurable { private static final String LIST_HOST_IDS_BY_HOST_TAGS = "SELECT filtered.host_id, COUNT(filtered.tag) AS tag_count " + "FROM (SELECT host_id, tag, is_tag_a_rule FROM host_tags GROUP BY host_id,tag,is_tag_a_rule) AS filtered " @@ -644,16 +646,22 @@ private void resetHosts(long managementServerId, long lastPingSecondsAfter) { sc.setParameters("lastPinged", lastPingSecondsAfter); sc.setParameters("status", Status.Disconnected, Status.Down, Status.Alert); - StringBuilder sb = new StringBuilder(); - List hosts = lockRows(sc, null, true); // exclusive lock - for (HostVO host : hosts) { - host.setManagementServerId(null); - update(host.getId(), host); - sb.append(host.getId()); - sb.append(" "); + // SELECT before bulk UPDATE to preserve per-host-ID trace logging — the bulk UPDATE + // cannot return which rows it matched since the WHERE column is being set to NULL + if (logger.isTraceEnabled()) { + List hosts = listBy(sc); + StringBuilder sb = new StringBuilder(); + for (HostVO host : hosts) { + sb.append(host.getId()); + sb.append(" "); + } + logger.trace("Following hosts will be reset: {}", sb); } - logger.trace("Following hosts got reset: {}", sb); + HostVO host = createForUpdate(); + host.setManagementServerId(null); + UpdateBuilder ub = getUpdateBuilder(host); + update(ub, sc, null); } /* @@ -1333,6 +1341,14 @@ public List listIdsForUpEnabledByZoneAndHypervisor(Long zoneId, Hypervisor return listIdsBy(null, Status.Up, ResourceState.Enabled, hypervisorType, zoneId, null, null); } + @Override + public List findRoutingByClusterId(Long clusterId) { + SearchCriteria sc = ClusterSearch.create(); + sc.setParameters("clusterId", clusterId); + sc.setParameters("type", Type.Routing); + return listBy(sc); + } + @Override public List findByClusterIdAndEncryptionSupport(Long clusterId) { SearchBuilder hostCapabilitySearch = _detailsDao.createSearchBuilder(); @@ -1412,7 +1428,7 @@ public HostVO findAnyStateHypervisorHostInCluster(long clusterId) { SearchCriteria sc = TypeStatusStateSearch.create(); sc.setParameters("type", Host.Type.Routing); sc.setParameters("cluster", clusterId); - List list = listBy(sc, new Filter(1)); + List list = listBy(sc, new Filter(1, true)); return list.isEmpty() ? null : list.get(0); } @@ -1448,6 +1464,16 @@ public List listAllHostsByZoneAndHypervisorType(long zoneId, HypervisorT return listBy(sc); } + @Override + public List listAllRoutingHostsByZoneAndHypervisorType(long zoneId, HypervisorType hypervisorType) { + SearchCriteria sc = DcSearch.create(); + sc.setParameters("dc", zoneId); + sc.setParameters("hypervisorType", hypervisorType.toString()); + sc.setParameters("type", Type.Routing); + + return listBy(sc); + } + @Override public List listAllHostsThatHaveNoRuleTag(Type type, Long clusterId, Long podId, Long dcId) { SearchCriteria sc = searchBuilderFindByIdTypeClusterIdPodIdDcIdAndWithoutRuleTag.create(); @@ -1520,11 +1546,13 @@ private List findHostIdsByHostTags(String hostTags){ } } - public List findHostsWithTagRuleThatMatchComputeOferringTags(String computeOfferingTags) { + @Override + public List findHostsWithTagRuleThatMatchComputeOfferingTags(String computeOfferingTags) { List hostTagVOList = _hostTagsDao.findHostRuleTags(); List result = new ArrayList<>(); for (HostTagVO rule: hostTagVOList) { - if (TagAsRuleHelper.interpretTagAsRule(rule.getTag(), computeOfferingTags, HostTagsDao.hostTagRuleExecutionTimeout.value())) { + if (GenericRuleHelper.interpretTagAsRule(rule.getTag(), computeOfferingTags, HostTagsDao.hostTagRuleExecutionTimeout.value(), + HostTagsDao.hostTagRuleExecutionTimeout.key())) { result.add(findById(rule.getHostId())); } } @@ -1532,9 +1560,26 @@ public List findHostsWithTagRuleThatMatchComputeOferringTags(String comp return result; } + @Override + public List findHostsWithGuestOsRulesThatDidNotMatchOsOfGuestVm(String templateGuestOSName) { + List hostIdsWithGuestOsRule = _detailsDao.findByName(Host.GUEST_OS_RULE); + List hostsWithIncompatibleRules = new ArrayList<>(); + for (DetailVO guestOsRule : hostIdsWithGuestOsRule) { + if (!GenericRuleHelper.interpretGuestOsRule(guestOsRule.getValue(), templateGuestOSName, HostDao.guestOsRuleExecutionTimeout.value(), + HostDao.guestOsRuleExecutionTimeout.key())) { + logger.trace("The guest OS rule [{}] of the host with ID [{}] is incompatible with the OS of the VM.", + guestOsRule.getHostId(), guestOsRule.getValue()); + hostsWithIncompatibleRules.add(findById(guestOsRule.getHostId())); + } + } + logger.trace("The hosts with the following IDs [{}] are incompatible with the VM considering their guest OS rule.", + hostsWithIncompatibleRules); + return hostsWithIncompatibleRules; + } + public List findClustersThatMatchHostTagRule(String computeOfferingTags) { Set result = new HashSet<>(); - List hosts = findHostsWithTagRuleThatMatchComputeOferringTags(computeOfferingTags); + List hosts = findHostsWithTagRuleThatMatchComputeOfferingTags(computeOfferingTags); for (HostVO host: hosts) { result.add(host.getClusterId()); } @@ -1984,4 +2029,14 @@ public List listDistinctStorageAccessGroups(String name, String keyword) return customSearch(sc, null); } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[] {guestOsRuleExecutionTimeout}; + } + + @Override + public String getConfigComponentName() { + return HostDaoImpl.class.getSimpleName(); + } } 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 5d8bd0a0a3a8..b72ec5239ffb 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 @@ -33,6 +33,8 @@ public interface HostDetailsDao extends GenericDao { List findByName(String name); + void removeExternalDetails(long hostId); + 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 3eb9faeb1c1a..0f8e8623506d 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 @@ -39,6 +39,7 @@ public class HostDetailsDaoImpl extends GenericDaoBase implement protected final SearchBuilder HostSearch; protected final SearchBuilder DetailSearch; protected final SearchBuilder DetailNameSearch; + protected final SearchBuilder ExternalDetailSearch; public HostDetailsDaoImpl() { HostSearch = createSearchBuilder(); @@ -53,6 +54,11 @@ public HostDetailsDaoImpl() { DetailNameSearch = createSearchBuilder(); DetailNameSearch.and("name", DetailNameSearch.entity().getName(), SearchCriteria.Op.EQ); DetailNameSearch.done(); + + ExternalDetailSearch = createSearchBuilder(); + ExternalDetailSearch.and("hostId", ExternalDetailSearch.entity().getHostId(), SearchCriteria.Op.EQ); + ExternalDetailSearch.and("name", ExternalDetailSearch.entity().getName(), SearchCriteria.Op.LIKE); + ExternalDetailSearch.done(); } @Override @@ -133,6 +139,17 @@ public List findByName(String name) { return listBy(sc); } + @Override + public void removeExternalDetails(long hostId) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + txn.start(); + SearchCriteria sc = ExternalDetailSearch.create(); + sc.setParameters("hostId", hostId); + sc.setParameters("name", VmDetailConstants.EXTERNAL_DETAIL_PREFIX + "%"); + remove(sc); + txn.commit(); + } + @Override public void replaceExternalDetails(long hostId, Map details) { if (details.isEmpty()) { @@ -149,11 +166,7 @@ public void replaceExternalDetails(long hostId, Map details) { } 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(); + SearchCriteria sc = ExternalDetailSearch.create(); sc.setParameters("hostId", hostId); sc.setParameters("name", VmDetailConstants.EXTERNAL_DETAIL_PREFIX + "%"); remove(sc); diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostTagsDao.java b/engine/schema/src/main/java/com/cloud/host/dao/HostTagsDao.java index 7a00829fd44e..0d86ca0e48c7 100644 --- a/engine/schema/src/main/java/com/cloud/host/dao/HostTagsDao.java +++ b/engine/schema/src/main/java/com/cloud/host/dao/HostTagsDao.java @@ -45,4 +45,9 @@ public interface HostTagsDao extends GenericDao { HostTagResponse newHostTagResponse(HostTagVO hostTag); List searchByIds(Long... hostTagIds); + + /** + * List all host tags defined on hosts within a cluster + */ + List listByClusterId(Long clusterId); } diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostTagsDaoImpl.java b/engine/schema/src/main/java/com/cloud/host/dao/HostTagsDaoImpl.java index 4aa14a31cfcf..d3fee6a26761 100644 --- a/engine/schema/src/main/java/com/cloud/host/dao/HostTagsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/host/dao/HostTagsDaoImpl.java @@ -23,6 +23,7 @@ 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.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; @@ -43,9 +44,12 @@ public class HostTagsDaoImpl extends GenericDaoBase implements private final SearchBuilder stSearch; private final SearchBuilder tagIdsearch; private final SearchBuilder ImplicitTagsSearch; + private final GenericSearchBuilder tagSearch; @Inject private ConfigurationDao _configDao; + @Inject + private HostDao hostDao; public HostTagsDaoImpl() { HostSearch = createSearchBuilder(); @@ -72,6 +76,11 @@ public HostTagsDaoImpl() { ImplicitTagsSearch.and("hostId", ImplicitTagsSearch.entity().getHostId(), SearchCriteria.Op.EQ); ImplicitTagsSearch.and("isImplicit", ImplicitTagsSearch.entity().getIsImplicit(), SearchCriteria.Op.EQ); ImplicitTagsSearch.done(); + + tagSearch = createSearchBuilder(String.class); + tagSearch.selectFields(tagSearch.entity().getTag()); + tagSearch.and("hostIdIN", tagSearch.entity().getHostId(), SearchCriteria.Op.IN); + tagSearch.done(); } @Override @@ -235,4 +244,15 @@ public List searchByIds(Long... tagIds) { return tagList; } + + @Override + public List listByClusterId(Long clusterId) { + List hostIds = hostDao.listIdsByClusterId(clusterId); + if (CollectionUtils.isEmpty(hostIds)) { + return new ArrayList<>(); + } + SearchCriteria sc = tagSearch.create(); + sc.setParameters("hostIdIN", hostIds.toArray()); + return customSearch(sc, null); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/LBHealthCheckPolicyVO.java b/engine/schema/src/main/java/com/cloud/network/LBHealthCheckPolicyVO.java index ee5f67b09cd1..23f0b0f15ae3 100644 --- a/engine/schema/src/main/java/com/cloud/network/LBHealthCheckPolicyVO.java +++ b/engine/schema/src/main/java/com/cloud/network/LBHealthCheckPolicyVO.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.network; +import java.util.Date; import java.util.UUID; import javax.persistence.Column; @@ -25,8 +26,11 @@ import javax.persistence.Id; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; import com.cloud.network.rules.HealthCheckPolicy; +import com.cloud.utils.db.GenericDao; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @@ -68,6 +72,10 @@ public class LBHealthCheckPolicyVO implements HealthCheckPolicy { @Column(name = "display", updatable = true, nullable = false) protected boolean display = true; + @Column(name = GenericDao.REMOVED_COLUMN) + @Temporal(value = TemporalType.TIMESTAMP) + private Date removed; + protected LBHealthCheckPolicyVO() { this.uuid = UUID.randomUUID().toString(); } diff --git a/engine/schema/src/main/java/com/cloud/network/as/dao/CounterDao.java b/engine/schema/src/main/java/com/cloud/network/as/dao/CounterDao.java index 4c9df8e749fe..372dc930b683 100644 --- a/engine/schema/src/main/java/com/cloud/network/as/dao/CounterDao.java +++ b/engine/schema/src/main/java/com/cloud/network/as/dao/CounterDao.java @@ -24,6 +24,7 @@ import com.cloud.utils.db.GenericDao; public interface CounterDao extends GenericDao { + CounterVO findByNameProviderValue(String name, String value, String provider); public List listCounters(Long id, String name, String source, String provider, String keyword, Filter filter); } diff --git a/engine/schema/src/main/java/com/cloud/network/as/dao/CounterDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/as/dao/CounterDaoImpl.java index 2d3906b83414..0badbe449acc 100644 --- a/engine/schema/src/main/java/com/cloud/network/as/dao/CounterDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/as/dao/CounterDaoImpl.java @@ -32,6 +32,7 @@ @Component public class CounterDaoImpl extends GenericDaoBase implements CounterDao { final SearchBuilder AllFieldsSearch; + final SearchBuilder CounterValueSearch; protected CounterDaoImpl() { AllFieldsSearch = createSearchBuilder(); @@ -40,6 +41,21 @@ protected CounterDaoImpl() { AllFieldsSearch.and("source", AllFieldsSearch.entity().getSource(), Op.EQ); AllFieldsSearch.and("provider", AllFieldsSearch.entity().getProvider(), Op.EQ); AllFieldsSearch.done(); + + CounterValueSearch = createSearchBuilder(); + CounterValueSearch.and("name", CounterValueSearch.entity().getName(), Op.EQ); + CounterValueSearch.and("value", CounterValueSearch.entity().getValue(), Op.EQ); + CounterValueSearch.and("provider", CounterValueSearch.entity().getProvider(), Op.EQ); + CounterValueSearch.done(); + } + + @Override + public CounterVO findByNameProviderValue(String name, String value, String provider) { + SearchCriteria sc = CounterValueSearch.create(); + sc.setParameters("name", name); + sc.setParameters("value", value); + sc.setParameters("provider", provider); + return findOneBy(sc); } @Override 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 7f322ae6c037..3527ce84dcf6 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 @@ -69,6 +69,8 @@ public interface FirewallRulesDao extends GenericDao { List listByNetworkPurposeTrafficType(long networkId, FirewallRule.Purpose purpose, FirewallRule.TrafficType trafficType); + List listByVpcPurposeTrafficType(long vpcId, FirewallRule.Purpose purpose, FirewallRule.TrafficType trafficType); + List listByIpAndPurposeWithState(Long addressId, FirewallRule.Purpose purpose, FirewallRule.State state); void loadSourceCidrs(FirewallRuleVO rule); 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 27bf7ba6aa83..5a1e1aae6b66 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 @@ -74,6 +74,7 @@ protected FirewallRulesDaoImpl() { AllFieldsSearch.and("domain", AllFieldsSearch.entity().getDomainId(), Op.EQ); AllFieldsSearch.and("id", AllFieldsSearch.entity().getId(), Op.EQ); AllFieldsSearch.and("networkId", AllFieldsSearch.entity().getNetworkId(), Op.EQ); + AllFieldsSearch.and("vpcId", AllFieldsSearch.entity().getVpcId(), Op.EQ); AllFieldsSearch.and("related", AllFieldsSearch.entity().getRelated(), Op.EQ); AllFieldsSearch.and("trafficType", AllFieldsSearch.entity().getTrafficType(), Op.EQ); AllFieldsSearch.done(); @@ -94,6 +95,7 @@ protected FirewallRulesDaoImpl() { ReleaseSearch.and("ipId", ReleaseSearch.entity().getSourceIpAddressId(), Op.EQ); ReleaseSearch.and("purpose", ReleaseSearch.entity().getPurpose(), Op.EQ); ReleaseSearch.and("ports", ReleaseSearch.entity().getSourcePortStart(), Op.IN); + ReleaseSearch.and("removed", ReleaseSearch.entity().getRemoved(), Op.NULL); ReleaseSearch.done(); SystemRuleSearch = createSearchBuilder(); @@ -355,6 +357,22 @@ public List listByNetworkPurposeTrafficType(long networkId, Purp return listBy(sc); } + @Override + public List listByVpcPurposeTrafficType(long vpcId, Purpose purpose, TrafficType trafficType) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("vpcId", vpcId); + + if (purpose != null) { + sc.setParameters("purpose", purpose); + } + + if (trafficType != null) { + sc.setParameters("trafficType", trafficType); + } + + return listBy(sc); + } + @Override @DB public boolean remove(Long id) { diff --git a/engine/schema/src/main/java/com/cloud/network/dao/LBHealthCheckPolicyDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/LBHealthCheckPolicyDaoImpl.java index 45927f3c3775..a9b4e02ba87f 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/LBHealthCheckPolicyDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/LBHealthCheckPolicyDaoImpl.java @@ -32,8 +32,9 @@ public class LBHealthCheckPolicyDaoImpl extends GenericDaoBase sc = createSearchCriteria(); sc.addAnd("loadBalancerId", SearchCriteria.Op.EQ, loadBalancerId); + sc.addAnd("removed", SearchCriteria.Op.NULL); - expunge(sc); + remove(sc); } @Override @@ -41,8 +42,9 @@ public void remove(long loadBalancerId, Boolean revoke) { SearchCriteria sc = createSearchCriteria(); sc.addAnd("loadBalancerId", SearchCriteria.Op.EQ, loadBalancerId); sc.addAnd("revoke", SearchCriteria.Op.EQ, revoke); + sc.addAnd("removed", SearchCriteria.Op.NULL); - expunge(sc); + remove(sc); } @Override diff --git a/engine/schema/src/main/java/com/cloud/network/dao/LBStickinessPolicyDao.java b/engine/schema/src/main/java/com/cloud/network/dao/LBStickinessPolicyDao.java index 6669d70824a7..03fadbbd6598 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/LBStickinessPolicyDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/LBStickinessPolicyDao.java @@ -28,4 +28,6 @@ public interface LBStickinessPolicyDao extends GenericDao listByLoadBalancerIdAndDisplayFlag(long loadBalancerId, boolean forDisplay); List listByLoadBalancerId(long loadBalancerId, boolean revoke); + + List listByLoadBalancerId(long loadBalancerId); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/LBStickinessPolicyDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/LBStickinessPolicyDaoImpl.java index 717458fdb99f..86fd908b6b76 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/LBStickinessPolicyDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/LBStickinessPolicyDaoImpl.java @@ -31,8 +31,9 @@ public class LBStickinessPolicyDaoImpl extends GenericDaoBase sc = createSearchCriteria(); sc.addAnd("loadBalancerId", SearchCriteria.Op.EQ, loadBalancerId); + sc.addAnd("removed", SearchCriteria.Op.NULL); - expunge(sc); + remove(sc); } @Override @@ -40,8 +41,9 @@ public void remove(long loadBalancerId, Boolean revoke) { SearchCriteria sc = createSearchCriteria(); sc.addAnd("loadBalancerId", SearchCriteria.Op.EQ, loadBalancerId); sc.addAnd("revoke", SearchCriteria.Op.EQ, revoke); + sc.addAnd("removed", SearchCriteria.Op.NULL); - expunge(sc); + remove(sc); } @Override @@ -62,4 +64,10 @@ public List listByLoadBalancerId(long loadBalancerId, bool return listBy(sc); } + @Override + public List listByLoadBalancerId(long loadBalancerId) { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("loadBalancerId", SearchCriteria.Op.EQ, loadBalancerId); + return listBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/LBStickinessPolicyVO.java b/engine/schema/src/main/java/com/cloud/network/dao/LBStickinessPolicyVO.java index 72b8fc151b78..ae658f0757f0 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/LBStickinessPolicyVO.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/LBStickinessPolicyVO.java @@ -17,6 +17,7 @@ package com.cloud.network.dao; import java.util.ArrayList; +import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -30,9 +31,12 @@ import javax.persistence.Id; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; import com.cloud.network.rules.StickinessPolicy; import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @@ -68,6 +72,10 @@ public class LBStickinessPolicyVO implements StickinessPolicy { @Column(name = "display", updatable = true, nullable = false) protected boolean display = true; + @Column(name = GenericDao.REMOVED_COLUMN) + @Temporal(value = TemporalType.TIMESTAMP) + private Date removed; + protected LBStickinessPolicyVO() { this.uuid = UUID.randomUUID().toString(); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/LoadBalancerCertMapVO.java b/engine/schema/src/main/java/com/cloud/network/dao/LoadBalancerCertMapVO.java index f95c61733f28..7fbe58df930c 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/LoadBalancerCertMapVO.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/LoadBalancerCertMapVO.java @@ -16,13 +16,17 @@ // under the License. package com.cloud.network.dao; +import java.util.Date; import java.util.UUID; 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 com.cloud.utils.db.GenericDao; import org.apache.cloudstack.api.InternalIdentity; @Entity @@ -45,6 +49,10 @@ public class LoadBalancerCertMapVO implements InternalIdentity { @Column(name = "revoke") private boolean revoke = false; + @Column(name = GenericDao.REMOVED_COLUMN) + @Temporal(value = TemporalType.TIMESTAMP) + private Date removed; + public LoadBalancerCertMapVO() { this.uuid = UUID.randomUUID().toString(); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/LoadBalancerVMMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/LoadBalancerVMMapDaoImpl.java index dc37cdeefe3d..03a1e45d917e 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/LoadBalancerVMMapDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/LoadBalancerVMMapDaoImpl.java @@ -34,8 +34,9 @@ public class LoadBalancerVMMapDaoImpl extends GenericDaoBase sc = createSearchCriteria(); sc.addAnd("loadBalancerId", SearchCriteria.Op.EQ, loadBalancerId); + sc.addAnd("removed", SearchCriteria.Op.NULL); - expunge(sc); + remove(sc); } @Override @@ -43,11 +44,12 @@ public void remove(long loadBalancerId, List instanceIds, Boolean revoke) SearchCriteria sc = createSearchCriteria(); sc.addAnd("loadBalancerId", SearchCriteria.Op.EQ, loadBalancerId); sc.addAnd("instanceId", SearchCriteria.Op.IN, instanceIds.toArray()); + sc.addAnd("removed", SearchCriteria.Op.NULL); if (revoke != null) { sc.addAnd("revoke", SearchCriteria.Op.EQ, revoke); } - expunge(sc); + remove(sc); } @Override @@ -56,12 +58,13 @@ public void remove(long loadBalancerId, long instanceId, String instanceIp, Bool sc.addAnd("loadBalancerId", SearchCriteria.Op.EQ, loadBalancerId); sc.addAnd("instanceId", SearchCriteria.Op.IN, instanceId); sc.addAnd("instanceIp", SearchCriteria.Op.EQ, instanceIp); + sc.addAnd("removed", SearchCriteria.Op.NULL); if (revoke != null) { sc.addAnd("revoke", SearchCriteria.Op.EQ, revoke); } - expunge(sc); + remove(sc); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/LoadBalancerVMMapVO.java b/engine/schema/src/main/java/com/cloud/network/dao/LoadBalancerVMMapVO.java index 721349613d88..2c88bf9c5a60 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/LoadBalancerVMMapVO.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/LoadBalancerVMMapVO.java @@ -22,9 +22,14 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import com.cloud.utils.db.GenericDao; import org.apache.cloudstack.api.InternalIdentity; +import java.util.Date; + @Entity @Table(name = "load_balancer_vm_map") public class LoadBalancerVMMapVO implements InternalIdentity { @@ -48,6 +53,10 @@ public class LoadBalancerVMMapVO implements InternalIdentity { @Column(name = "state") private String state; + @Column(name = GenericDao.REMOVED_COLUMN) + @Temporal(value = TemporalType.TIMESTAMP) + private Date removed = null; + public LoadBalancerVMMapVO() { } 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 ad0338b98497..3886529322e3 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 String toString() { 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/NetworkDao.java b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java index 0861a424ebb6..b656d896e37f 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java @@ -18,11 +18,13 @@ import java.util.List; import java.util.Map; +import java.util.Set; import com.cloud.network.Network; import com.cloud.network.Network.GuestType; import com.cloud.network.Network.State; import com.cloud.network.Networks.TrafficType; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDao; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.fsm.StateDao; @@ -47,6 +49,12 @@ public interface NetworkDao extends GenericDao, StateDao listByNetworkDomains(Set uniqueNtwkDomains); + + List listByNetworkDomainsAndAccountIds(Set uniqueNtwkDomains, Set accountIds); + + List listByNetworkDomainsAndDomainIds(Set uniqueNtwkDomains, Set domainIds); + /** * Retrieves the next available mac address in this network configuration. * @@ -89,8 +97,13 @@ public interface NetworkDao extends GenericDao, StateDao serviceProviderMap); + List listByZoneAndTrafficType(long zoneId, TrafficType trafficType, Filter filter); + List listByZoneAndTrafficType(long zoneId, TrafficType trafficType); + List listByZonesTrafficTypeAndOwners(List zoneIds, final TrafficType trafficType, + List accountIds, List domainIds, Filter filter); + void setCheckForGc(long networkId); int getNetworkCountByNetworkOffId(long networkOfferingId); @@ -128,4 +141,6 @@ public interface NetworkDao extends GenericDao, StateDao listByPhysicalNetworkPvlan(long physicalNetworkId, String broadcastUri); List getAllPersistentNetworksFromZone(long dataCenterId); + + NetworkVO findByZoneIdAndAccountIdAndGuestTypeAndName(long zoneId, long accountId, GuestType guestType, String name); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java index 0aae532eac57..fbf6a2ac2261 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java @@ -19,6 +19,7 @@ import java.net.URI; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -29,15 +30,14 @@ import javax.inject.Inject; import javax.persistence.TableGenerator; -import com.cloud.utils.exception.CloudRuntimeException; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.api.ApiConstants; +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.network.Network; import com.cloud.network.Network.Event; import com.cloud.network.Network.GuestType; -import com.cloud.network.Network.Provider; import com.cloud.network.Network.Service; import com.cloud.network.Network.State; import com.cloud.network.Networks.BroadcastDomainType; @@ -63,6 +63,7 @@ import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.SequenceFetcher; import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; @Component @@ -86,6 +87,7 @@ public class NetworkDaoImpl extends GenericDaoBaseimplements Ne GenericSearchBuilder GarbageCollectedSearch; SearchBuilder PrivateNetworkSearch; + SearchBuilder NetworkDomainSearch; @Inject ResourceTagDao _tagsDao; @@ -192,12 +194,19 @@ protected void init() { PersistentNetworkSearch.and("id", PersistentNetworkSearch.entity().getId(), Op.NEQ); PersistentNetworkSearch.and("guestType", PersistentNetworkSearch.entity().getGuestType(), Op.IN); PersistentNetworkSearch.and("broadcastUri", PersistentNetworkSearch.entity().getBroadcastUri(), Op.EQ); + PersistentNetworkSearch.and("dc", PersistentNetworkSearch.entity().getDataCenterId(), Op.EQ); PersistentNetworkSearch.and("removed", PersistentNetworkSearch.entity().getRemoved(), Op.NULL); final SearchBuilder persistentNtwkOffJoin = _ntwkOffDao.createSearchBuilder(); persistentNtwkOffJoin.and("persistent", persistentNtwkOffJoin.entity().isPersistent(), Op.EQ); PersistentNetworkSearch.join("persistent", persistentNtwkOffJoin, PersistentNetworkSearch.entity().getNetworkOfferingId(), persistentNtwkOffJoin.entity().getId(), JoinType.INNER); PersistentNetworkSearch.done(); + NetworkDomainSearch = createSearchBuilder(); + NetworkDomainSearch.and("networkDomains", NetworkDomainSearch.entity().getNetworkDomain(), Op.IN); + NetworkDomainSearch.and("accounts", NetworkDomainSearch.entity().getAccountId(), Op.IN); + NetworkDomainSearch.and("domains", NetworkDomainSearch.entity().getDomainId(), Op.IN); + NetworkDomainSearch.done(); + PhysicalNetworkSearch = createSearchBuilder(); PhysicalNetworkSearch.and("physicalNetworkId", PhysicalNetworkSearch.entity().getPhysicalNetworkId(), Op.EQ); PhysicalNetworkSearch.done(); @@ -382,7 +391,7 @@ public void persistNetworkServiceProviders(final long networkId, final Map getAllPersistentNetworksFromZone(long dataCenterId) { return search(sc, null); } + @Override + public List listByNetworkDomains(Set uniqueNtwkDomains) { + SearchCriteria sc = NetworkDomainSearch.create(); + sc.setParameters("networkDomains", uniqueNtwkDomains.toArray()); + return search(sc, null); + } + + @Override + public List listByNetworkDomainsAndAccountIds(Set uniqueNtwkDomains, Set accountIds) { + SearchCriteria sc = NetworkDomainSearch.create(); + sc.setParameters("networkDomains", uniqueNtwkDomains.toArray()); + sc.setParameters("accounts", accountIds.toArray()); + return search(sc, null); + } + + @Override + public List listByNetworkDomainsAndDomainIds(Set uniqueNtwkDomains, Set domainIds) { + SearchCriteria sc = NetworkDomainSearch.create(); + sc.setParameters("networkDomains", uniqueNtwkDomains.toArray()); + sc.setParameters("domains", domainIds.toArray()); + return search(sc, null); + } + @Override public String getNextAvailableMacAddress(final long networkConfigId, Integer zoneMacIdentifier) { final SequenceFetcher fetch = SequenceFetcher.getInstance(); long seq = fetch.getNextSequence(Long.class, _tgMacAddress, networkConfigId); - if(zoneMacIdentifier != null && zoneMacIdentifier.intValue() != 0 ){ - seq = seq | _prefix << 40 | (long)zoneMacIdentifier << 32 | networkConfigId << 16 & 0x00000000ffff0000l; + if (zoneMacIdentifier != null && zoneMacIdentifier != 0) { + seq = seq | _prefix << 40 | (long)zoneMacIdentifier << 32 | networkConfigId << 16 & 0x00000000ffff0000L; } return NetUtils.long2Mac(seq); } @@ -568,7 +600,7 @@ public List listByPhysicalNetwork(final long physicalNetworkId) { public List listByPhysicalNetworkTrafficType(final long physicalNetworkId, final TrafficType trafficType) { final SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("trafficType", trafficType); - sc.setParameters("physicalNetwork", physicalNetworkId); + sc.setParameters("physicalNetworkId", physicalNetworkId); return listBy(sc); } @@ -601,12 +633,46 @@ public List listBy(final long accountId, final long dataCenterId, fin } @Override - public List listByZoneAndTrafficType(final long zoneId, final TrafficType trafficType) { + public List listByZoneAndTrafficType(final long zoneId, final TrafficType trafficType, Filter filter) { final SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("datacenter", zoneId); sc.setParameters("trafficType", trafficType); - return listBy(sc, null); + return listBy(sc, filter); + } + + @Override + public List listByZoneAndTrafficType(final long zoneId, final TrafficType trafficType) { + return listByZoneAndTrafficType(zoneId, trafficType, null); + } + + @Override + public List listByZonesTrafficTypeAndOwners(List zoneIds, final TrafficType trafficType, + List accountIds, List domainIds, Filter filter) { + if (CollectionUtils.isEmpty(zoneIds)) { + return Collections.emptyList(); + } + SearchBuilder sb = createSearchBuilder(); + sb.and("dataCenterId", sb.entity().getDataCenterId(), SearchCriteria.Op.IN); + sb.and("trafficType", sb.entity().getTrafficType(), Op.EQ); + boolean accountIdsNotEmpty = CollectionUtils.isNotEmpty(accountIds); + boolean domainIdsNotEmpty = CollectionUtils.isNotEmpty(domainIds); + if (accountIdsNotEmpty || domainIdsNotEmpty) { + sb.and().op("account", sb.entity().getAccountId(), SearchCriteria.Op.IN); + sb.or("domain", sb.entity().getDomainId(), SearchCriteria.Op.IN); + sb.cp(); + } + sb.done(); + final SearchCriteria sc = sb.create(); + sc.setParameters("dataCenterId", zoneIds.toArray()); + sc.setParameters("trafficType", trafficType); + if (accountIdsNotEmpty) { + sc.setParameters("account", accountIds.toArray()); + } + if (domainIdsNotEmpty) { + sc.setParameters("domain", domainIds.toArray()); + } + return listBy(sc, filter); } @Override @@ -877,4 +943,16 @@ public List listByPhysicalNetworkPvlan(long physicalNetworkId, String return overlappingNetworks; } + + @Override + public NetworkVO findByZoneIdAndAccountIdAndGuestTypeAndName(long zoneId, long accountId, GuestType guestType, String name) { + SearchCriteria sc = AllFieldsSearch.create(); + + sc.setParameters("datacenter", zoneId); + sc.setParameters("account", accountId); + sc.setParameters("guestType", guestType); + sc.setParameters("name", name); + + return findOneBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetworkServiceMapVO.java b/engine/schema/src/main/java/com/cloud/network/dao/NetworkServiceMapVO.java index 4e1e9aff4561..65598eda7399 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/NetworkServiceMapVO.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetworkServiceMapVO.java @@ -81,6 +81,12 @@ public NetworkServiceMapVO(long networkId, Service service, Provider provider) { this.provider = provider.getName(); } + public NetworkServiceMapVO(long networkId, String serviceName, String providerName) { + this.networkId = networkId; + this.service = serviceName; + this.provider = providerName; + } + @Override public String toString() { StringBuilder buf = new StringBuilder("[Network Service["); diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetworkVO.java b/engine/schema/src/main/java/com/cloud/network/dao/NetworkVO.java index 02abaacd854e..f2572ba91c21 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/NetworkVO.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetworkVO.java @@ -203,9 +203,13 @@ public class NetworkVO implements Network { @Column(name = "private_mtu") Integer privateMtu; + @Column(name = "keep_mac_address_on_public_nic") + private boolean keepMacAddressOnPublicNic = true; + @Transient Integer networkCidrSize; + public NetworkVO() { uuid = UUID.randomUUID().toString(); } @@ -773,4 +777,13 @@ public Integer getNetworkCidrSize() { public void setNetworkCidrSize(Integer networkCidrSize) { this.networkCidrSize = networkCidrSize; } + + @Override + public boolean getKeepMacAddressOnPublicNic() { + return keepMacAddressOnPublicNic; + } + + public void setKeepMacAddressOnPublicNic(boolean keepMacAddressOnPublicNic) { + this.keepMacAddressOnPublicNic = keepMacAddressOnPublicNic; + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/PhysicalNetworkServiceProviderVO.java b/engine/schema/src/main/java/com/cloud/network/dao/PhysicalNetworkServiceProviderVO.java index 9557c7465bff..4be6c5bc344d 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/PhysicalNetworkServiceProviderVO.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/PhysicalNetworkServiceProviderVO.java @@ -97,6 +97,9 @@ public class PhysicalNetworkServiceProviderVO implements PhysicalNetworkServiceP @Column(name = "networkacl_service_provided") boolean networkAclServiceProvided; + @Column(name = "custom_action_service_provided") + boolean customActionServiceProvided; + @Column(name = GenericDao.REMOVED_COLUMN) Date removed; @@ -278,6 +281,7 @@ public void setEnabledServices(List services) { this.setUserdataServiceProvided(services.contains(Service.UserData)); this.setSecuritygroupServiceProvided(services.contains(Service.SecurityGroup)); this.setNetworkAclServiceProvided(services.contains(Service.NetworkACL)); + this.setCustomActionServiceProvided(services.contains(Service.CustomAction)); } @Override @@ -301,21 +305,27 @@ public List getEnabledServices() { if (this.isLbServiceProvided()) { services.add(Service.Lb); } - if (this.sourcenatServiceProvided) { + if (this.isSourcenatServiceProvided()) { services.add(Service.SourceNat); } - if (this.staticnatServiceProvided) { + if (this.isStaticnatServiceProvided()) { services.add(Service.StaticNat); } - if (this.portForwardingServiceProvided) { + if (this.isPortForwardingServiceProvided()) { services.add(Service.PortForwarding); } + if (this.isNetworkAclServiceProvided()) { + services.add(Service.NetworkACL); + } if (this.isUserdataServiceProvided()) { services.add(Service.UserData); } if (this.isSecuritygroupServiceProvided()) { services.add(Service.SecurityGroup); } + if (this.isCustomActionServiceProvided()) { + services.add(Service.CustomAction); + } return services; } @@ -327,4 +337,12 @@ public boolean isNetworkAclServiceProvided() { public void setNetworkAclServiceProvided(boolean networkAclServiceProvided) { this.networkAclServiceProvided = networkAclServiceProvided; } + + public boolean isCustomActionServiceProvided() { + return customActionServiceProvided; + } + + public void setCustomActionServiceProvided(boolean customActionServiceProvided) { + this.customActionServiceProvided = customActionServiceProvided; + } } 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 09d9f1d7fbf9..fdd827ffeeef 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 @@ -137,7 +137,9 @@ public String getNetworkTag(long physicalNetworkId, TrafficType trafficType, Hyp } sc.setParameters("physicalNetworkId", physicalNetworkId); - sc.setParameters("trafficType", trafficType); + if (trafficType != null) { + sc.setParameters("trafficType", trafficType); + } List tag = customSearch(sc, null); return tag.size() == 0 ? null : tag.get(0); diff --git a/engine/schema/src/main/java/com/cloud/network/dao/PublicIpQuarantineDao.java b/engine/schema/src/main/java/com/cloud/network/dao/PublicIpQuarantineDao.java index ccba6bb18893..606bdaaaa7a7 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/PublicIpQuarantineDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/PublicIpQuarantineDao.java @@ -19,9 +19,21 @@ import com.cloud.network.vo.PublicIpQuarantineVO; import com.cloud.utils.db.GenericDao; +import java.util.Date; +import java.util.List; + public interface PublicIpQuarantineDao extends GenericDao { PublicIpQuarantineVO findByPublicIpAddressId(long publicIpAddressId); PublicIpQuarantineVO findByIpAddress(String publicIpAddress); + + /** + * Returns a list of public IP addresses that are actively quarantined at the specified date and the previous owner differs from the specified user. + * + * @param userId used to check against the IP's previous owner; + * @param date used to check if the quarantine is active; + * @return a list of PublicIpQuarantineVOs. + */ + List listQuarantinedIpAddressesToUser(Long userId, Date date); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/PublicIpQuarantineDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/PublicIpQuarantineDaoImpl.java index a1b789b8a46b..0c47a0d36e30 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/PublicIpQuarantineDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/PublicIpQuarantineDaoImpl.java @@ -26,6 +26,8 @@ import javax.annotation.PostConstruct; import javax.inject.Inject; +import java.util.Date; +import java.util.List; @Component public class PublicIpQuarantineDaoImpl extends GenericDaoBase implements PublicIpQuarantineDao { @@ -33,6 +35,8 @@ public class PublicIpQuarantineDaoImpl extends GenericDaoBase ipAddressSearchBuilder; + private SearchBuilder quarantinedIpAddressesSearch; + @Inject IPAddressDao ipAddressDao; @@ -47,8 +51,16 @@ public void init() { publicIpAddressByIdSearch.join("quarantineJoin", ipAddressSearchBuilder, ipAddressSearchBuilder.entity().getId(), publicIpAddressByIdSearch.entity().getPublicIpAddressId(), JoinBuilder.JoinType.INNER); + quarantinedIpAddressesSearch = createSearchBuilder(); + quarantinedIpAddressesSearch.and("previousOwnerId", quarantinedIpAddressesSearch.entity().getPreviousOwnerId(), SearchCriteria.Op.NEQ); + quarantinedIpAddressesSearch.and(); + quarantinedIpAddressesSearch.op("removedIsNull", quarantinedIpAddressesSearch.entity().getRemoved(), SearchCriteria.Op.NULL); + quarantinedIpAddressesSearch.and("endDate", quarantinedIpAddressesSearch.entity().getEndDate(), SearchCriteria.Op.GT); + quarantinedIpAddressesSearch.cp(); + ipAddressSearchBuilder.done(); publicIpAddressByIdSearch.done(); + quarantinedIpAddressesSearch.done(); } @Override @@ -68,4 +80,14 @@ public PublicIpQuarantineVO findByIpAddress(String publicIpAddress) { return findOneBy(sc, filter); } + + @Override + public List listQuarantinedIpAddressesToUser(Long userId, Date date) { + SearchCriteria sc = quarantinedIpAddressesSearch.create(); + + sc.setParameters("previousOwnerId", userId); + sc.setParameters("endDate", date); + + return searchIncludingRemoved(sc, null, false, false); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/RouterHealthCheckResultVO.java b/engine/schema/src/main/java/com/cloud/network/dao/RouterHealthCheckResultVO.java index 9803ccb6a4bd..204ef2d15381 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/RouterHealthCheckResultVO.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/RouterHealthCheckResultVO.java @@ -29,6 +29,7 @@ import javax.persistence.TemporalType; import com.cloud.network.RouterHealthCheckResult; +import com.cloud.network.VirtualNetworkApplianceService; import com.cloud.utils.StringUtils; @Entity @@ -49,7 +50,7 @@ public class RouterHealthCheckResultVO implements RouterHealthCheckResult { private String checkType; @Column(name = "check_result") - private boolean checkResult; + private VirtualNetworkApplianceService.RouterHealthStatus checkResult; @Temporal(TemporalType.TIMESTAMP) @Column(name = "last_update", updatable = true, nullable = true) @@ -87,7 +88,7 @@ public String getCheckType() { } @Override - public boolean getCheckResult() { + public VirtualNetworkApplianceService.RouterHealthStatus getCheckResult() { return checkResult; } @@ -105,7 +106,7 @@ public byte[] getCheckDetails() { return checkDetails; } - public void setCheckResult(boolean checkResult) { + public void setCheckResult(VirtualNetworkApplianceService.RouterHealthStatus checkResult) { this.checkResult = checkResult; } diff --git a/engine/schema/src/main/java/com/cloud/network/rules/FirewallRuleVO.java b/engine/schema/src/main/java/com/cloud/network/rules/FirewallRuleVO.java index 1dfdc5093a59..2b34e23fa4d4 100644 --- a/engine/schema/src/main/java/com/cloud/network/rules/FirewallRuleVO.java +++ b/engine/schema/src/main/java/com/cloud/network/rules/FirewallRuleVO.java @@ -32,6 +32,8 @@ import javax.persistence.Inheritance; import javax.persistence.InheritanceType; import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; import javax.persistence.Transient; import com.cloud.utils.db.GenericDao; @@ -82,9 +84,16 @@ public class FirewallRuleVO implements FirewallRule { @Column(name = GenericDao.CREATED_COLUMN) Date created; + @Column(name = GenericDao.REMOVED_COLUMN) + @Temporal(value = TemporalType.TIMESTAMP) + private Date removed; + @Column(name = "network_id") Long networkId; + @Column(name = "vpc_id") + Long vpcId; + @Column(name = "icmp_code") Integer icmpCode; @@ -190,10 +199,18 @@ public State getState() { } @Override - public long getNetworkId() { + public Long getNetworkId() { return networkId; } + public Long getVpcId() { + return vpcId; + } + + public void setVpcId(Long vpcId) { + this.vpcId = vpcId; + } + @Override public FirewallRuleType getType() { return type; @@ -203,11 +220,15 @@ public Date getCreated() { return created; } + public Date getRemoved() { + return removed; + } + protected FirewallRuleVO() { uuid = UUID.randomUUID().toString(); } - public FirewallRuleVO(String xId, Long ipAddressId, Integer portStart, Integer portEnd, String protocol, long networkId, long accountId, long domainId, + public FirewallRuleVO(String xId, Long ipAddressId, Integer portStart, Integer portEnd, String protocol, Long networkId, long accountId, long domainId, Purpose purpose, List sourceCidrs, Integer icmpCode, Integer icmpType, Long related, TrafficType trafficType) { this.xId = xId; if (xId == null) { @@ -251,7 +272,7 @@ public FirewallRuleVO(String xId, long ipAddressId, int port, String protocol, l } - public FirewallRuleVO(String xId, Long ipAddressId, Integer portStart, Integer portEnd, String protocol, long networkId, long accountId, long domainId, + public FirewallRuleVO(String xId, Long ipAddressId, Integer portStart, Integer portEnd, String protocol, Long networkId, long accountId, long domainId, Purpose purpose, List sourceCidrs, List destCidrs, Integer icmpCode, Integer icmpType, Long related, TrafficType trafficType) { this(xId,ipAddressId, portStart, portEnd, protocol, networkId, accountId, domainId, purpose, sourceCidrs, icmpCode, icmpType, related, trafficType); this.destinationCidrs = destCidrs; diff --git a/engine/schema/src/main/java/com/cloud/network/security/dao/SecurityGroupDao.java b/engine/schema/src/main/java/com/cloud/network/security/dao/SecurityGroupDao.java index 793334731095..2b18bcbd87bd 100644 --- a/engine/schema/src/main/java/com/cloud/network/security/dao/SecurityGroupDao.java +++ b/engine/schema/src/main/java/com/cloud/network/security/dao/SecurityGroupDao.java @@ -31,4 +31,6 @@ public interface SecurityGroupDao extends GenericDao { List findByAccountAndNames(Long accountId, String... names); int removeByAccountId(long accountId); + + List listByIds(List ids); } diff --git a/engine/schema/src/main/java/com/cloud/network/security/dao/SecurityGroupDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/security/dao/SecurityGroupDaoImpl.java index 019cf5fec462..ffb45629b625 100644 --- a/engine/schema/src/main/java/com/cloud/network/security/dao/SecurityGroupDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/security/dao/SecurityGroupDaoImpl.java @@ -129,4 +129,14 @@ public boolean expunge(Long id) { txn.commit(); return result; } + + @Override + public List listByIds(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 listBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/security/dao/SecurityGroupWorkDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/security/dao/SecurityGroupWorkDaoImpl.java index 327d12c759a7..3180ef30a3ce 100644 --- a/engine/schema/src/main/java/com/cloud/network/security/dao/SecurityGroupWorkDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/security/dao/SecurityGroupWorkDaoImpl.java @@ -116,7 +116,7 @@ public SecurityGroupWorkVO take(long serverId) { //ensure that there is no job in Processing state for the same VM processing = true; if (logger.isTraceEnabled()) { - logger.trace("Security Group work take: found a job in Scheduled and Processing vmid=" + work.getInstanceId()); + logger.trace("Security Group work take: found a job in Scheduled and Processing vmid={}", work.getInstanceId()); } } work.setServerId(serverId); @@ -141,26 +141,16 @@ public SecurityGroupWorkVO take(long serverId) { } @Override - @DB public void updateStep(Long vmId, Long logSequenceNumber, Step step) { - final TransactionLegacy txn = TransactionLegacy.currentTxn(); - txn.start(); SearchCriteria sc = VmIdSeqNumSearch.create(); sc.setParameters("vmId", vmId); sc.setParameters("seqno", logSequenceNumber); - final Filter filter = new Filter(SecurityGroupWorkVO.class, null, true, 0l, 1l); - - final List vos = lockRows(sc, filter, true); - if (vos.size() == 0) { - txn.commit(); - return; - } - SecurityGroupWorkVO work = vos.get(0); - work.setStep(step); - update(work.getId(), work); - - txn.commit(); + SecurityGroupWorkVO workForUpdate = createForUpdate(); + workForUpdate.setStep(step); + // LIMIT 1 preserves the original single-row semantics: op_nwgrp_work has no + // uniqueness on (instance_id, seq_no), so without it duplicate rows would all be updated. + update(workForUpdate, sc, 1); } @Override @@ -172,21 +162,10 @@ public SecurityGroupWorkVO findByVmIdStep(long vmId, Step step) { } @Override - @DB public void updateStep(Long workId, Step step) { - final TransactionLegacy txn = TransactionLegacy.currentTxn(); - txn.start(); - - SecurityGroupWorkVO work = lockRow(workId, true); - if (work == null) { - txn.commit(); - return; - } - work.setStep(step); - update(work.getId(), work); - - txn.commit(); - + SecurityGroupWorkVO workForUpdate = createForUpdate(); + workForUpdate.setStep(step); + update(workId, workForUpdate); } @Override diff --git a/engine/schema/src/main/java/com/cloud/network/security/dao/VmRulesetLogDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/security/dao/VmRulesetLogDaoImpl.java index 9a9ca80bce59..7ed0ad0bcc54 100644 --- a/engine/schema/src/main/java/com/cloud/network/security/dao/VmRulesetLogDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/security/dao/VmRulesetLogDaoImpl.java @@ -76,7 +76,6 @@ public VmRulesetLogVO findByVmId(long vmId) { @Override public int createOrUpdate(Set workItems) { - //return createOrUpdateUsingBatch(workItems); return createOrUpdateUsingMultiInsert(workItems); } 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 9320a37bc96e..b913468384e4 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 @@ -91,6 +91,9 @@ public class VpcOfferingVO implements VpcOffering { @Column(name = "specify_as_number") private Boolean specifyAsNumber = false; + @Column(name = "conserve_mode") + private boolean conserveMode; + public VpcOfferingVO() { this.uuid = UUID.randomUUID().toString(); } @@ -242,4 +245,13 @@ public Boolean isSpecifyAsNumber() { public void setSpecifyAsNumber(Boolean specifyAsNumber) { this.specifyAsNumber = specifyAsNumber; } + + @Override + public boolean isConserveMode() { + return conserveMode; + } + + public void setConserveMode(boolean conserveMode) { + this.conserveMode = conserveMode; + } } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/VpcServiceMapVO.java b/engine/schema/src/main/java/com/cloud/network/vpc/VpcServiceMapVO.java index 9fa4b505c650..ae156d7d4304 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/VpcServiceMapVO.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/VpcServiceMapVO.java @@ -25,8 +25,6 @@ import javax.persistence.Id; import javax.persistence.Table; -import com.cloud.network.Network.Provider; -import com.cloud.network.Network.Service; import com.cloud.utils.db.GenericDao; @Entity @@ -72,10 +70,10 @@ public Date getCreated() { public VpcServiceMapVO() { } - public VpcServiceMapVO(long vpcId, Service service, Provider provider) { + public VpcServiceMapVO(long vpcId, String serviceName, String providerName) { this.vpcId = vpcId; - this.service = service.getName(); - this.provider = provider.getName(); + this.service = serviceName; + this.provider = providerName; } @Override 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 e942eadb8ffb..742d3f2f82ee 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 @@ -108,6 +108,9 @@ public class VpcVO implements Vpc { @Column(name = "use_router_ip_resolver") boolean useRouterIpResolver = false; + @Column(name = "keep_mac_address_on_public_nic") + private boolean keepMacAddressOnPublicNic = true; + @Transient boolean rollingRestart = false; @@ -321,4 +324,13 @@ public boolean useRouterIpAsResolver() { public void setUseRouterIpResolver(boolean useRouterIpResolver) { this.useRouterIpResolver = useRouterIpResolver; } + + @Override + public boolean getKeepMacAddressOnPublicNic() { + return keepMacAddressOnPublicNic; + } + + public void setKeepMacAddressOnPublicNic(boolean keepMacAddressOnPublicNic) { + this.keepMacAddressOnPublicNic = keepMacAddressOnPublicNic; + } } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcDaoImpl.java index 4e13fe4f5d0d..cecdce1aba42 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcDaoImpl.java @@ -142,7 +142,7 @@ public void persistVpcServiceProviders(long vpcId, Map> ser txn.start(); for (String service : serviceProviderMap.keySet()) { for (String provider : serviceProviderMap.get(service)) { - VpcServiceMapVO serviceMap = new VpcServiceMapVO(vpcId, Network.Service.getService(service), Network.Provider.getProvider(provider)); + VpcServiceMapVO serviceMap = new VpcServiceMapVO(vpcId, Network.Service.getService(service).getName(), provider); _vpcSvcMap.persist(serviceMap); } } 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 020536e97ec9..4bc9ad646d38 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 @@ -42,4 +42,6 @@ public interface VpcOfferingServiceMapDao extends GenericDao listProvidersForServiceForVpcOffering(long vpcOfferingId, Service service); + List listOfferingIdsByServiceAndProvider(Service service, String provider); + } 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 dcb1becf9e88..de36b6c3219b 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 @@ -37,6 +37,7 @@ public class VpcOfferingServiceMapDaoImpl extends GenericDaoBase AllFieldsSearch; final SearchBuilder MultipleServicesSearch; final GenericSearchBuilder ServicesSearch; + final GenericSearchBuilder OfferingIdsByServiceAndProviderSearch; protected VpcOfferingServiceMapDaoImpl() { super(); @@ -56,6 +57,12 @@ protected VpcOfferingServiceMapDaoImpl() { ServicesSearch.and("offeringId", ServicesSearch.entity().getVpcOfferingId(), SearchCriteria.Op.EQ); ServicesSearch.select(null, Func.DISTINCT, ServicesSearch.entity().getService()); ServicesSearch.done(); + + OfferingIdsByServiceAndProviderSearch = createSearchBuilder(Long.class); + OfferingIdsByServiceAndProviderSearch.selectFields(OfferingIdsByServiceAndProviderSearch.entity().getVpcOfferingId()); + OfferingIdsByServiceAndProviderSearch.and("service", OfferingIdsByServiceAndProviderSearch.entity().getService(), SearchCriteria.Op.EQ); + OfferingIdsByServiceAndProviderSearch.and("provider", OfferingIdsByServiceAndProviderSearch.entity().getProvider(), SearchCriteria.Op.EQ); + OfferingIdsByServiceAndProviderSearch.done(); } @Override @@ -129,4 +136,12 @@ public List listProvidersForServiceForVpcOffering(long return customSearch(sc, null); } + + @Override + public List listOfferingIdsByServiceAndProvider(Service service, String provider) { + SearchCriteria sc = OfferingIdsByServiceAndProviderSearch.create(); + sc.setParameters("service", service.getName()); + sc.setParameters("provider", provider); + return customSearch(sc, null); + } } diff --git a/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingServiceMapDao.java b/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingServiceMapDao.java index 8fb5d6dff84b..5954f95b3c57 100644 --- a/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingServiceMapDao.java +++ b/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingServiceMapDao.java @@ -44,4 +44,6 @@ public interface NetworkOfferingServiceMapDao extends GenericDao listServicesForNetworkOffering(long networkOfferingId); List getDistinctProviders(long offId); + + List listOfferingIdsByServiceAndProvider(Service service, String provider); } diff --git a/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingServiceMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingServiceMapDaoImpl.java index 67b341a93618..5fe86760d50a 100644 --- a/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingServiceMapDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingServiceMapDaoImpl.java @@ -40,6 +40,7 @@ public class NetworkOfferingServiceMapDaoImpl extends GenericDaoBase ProvidersSearch; final GenericSearchBuilder ServicesSearch; final GenericSearchBuilder DistinctProvidersSearch; + final GenericSearchBuilder OfferingIdsByServiceAndProviderSearch; protected NetworkOfferingServiceMapDaoImpl() { super(); @@ -71,6 +72,12 @@ protected NetworkOfferingServiceMapDaoImpl() { DistinctProvidersSearch.and("provider", DistinctProvidersSearch.entity().getProvider(), SearchCriteria.Op.EQ); DistinctProvidersSearch.selectFields(DistinctProvidersSearch.entity().getProvider()); DistinctProvidersSearch.done(); + + OfferingIdsByServiceAndProviderSearch = createSearchBuilder(Long.class); + OfferingIdsByServiceAndProviderSearch.selectFields(OfferingIdsByServiceAndProviderSearch.entity().getNetworkOfferingId()); + OfferingIdsByServiceAndProviderSearch.and("service", OfferingIdsByServiceAndProviderSearch.entity().getService(), SearchCriteria.Op.EQ); + OfferingIdsByServiceAndProviderSearch.and("provider", OfferingIdsByServiceAndProviderSearch.entity().getProvider(), SearchCriteria.Op.EQ); + OfferingIdsByServiceAndProviderSearch.done(); } @Override @@ -170,4 +177,12 @@ public List getDistinctProviders(long offId) { List results = customSearch(sc, null); return results; } + + @Override + public List listOfferingIdsByServiceAndProvider(Service service, String provider) { + SearchCriteria sc = OfferingIdsByServiceAndProviderSearch.create(); + sc.setParameters("service", service.getName()); + sc.setParameters("provider", provider); + return customSearch(sc, null); + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/DiskOfferingVO.java b/engine/schema/src/main/java/com/cloud/storage/DiskOfferingVO.java index 79f5bcb51578..7f6b6d8adf0e 100644 --- a/engine/schema/src/main/java/com/cloud/storage/DiskOfferingVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/DiskOfferingVO.java @@ -577,11 +577,11 @@ public Integer getHypervisorSnapshotReserve() { @Override public void setEncrypt(boolean encrypt) { this.encrypt = encrypt; } + @Override public boolean isShared() { return !useLocalStorage; } - public boolean getDiskSizeStrictness() { return diskSizeStrictness; } diff --git a/engine/schema/src/main/java/com/cloud/storage/SnapshotPolicyVO.java b/engine/schema/src/main/java/com/cloud/storage/SnapshotPolicyVO.java index f57d9d3dccf2..299c6380ab63 100644 --- a/engine/schema/src/main/java/com/cloud/storage/SnapshotPolicyVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/SnapshotPolicyVO.java @@ -59,6 +59,12 @@ public class SnapshotPolicyVO implements SnapshotPolicy { @Column(name = "uuid") String uuid; + @Column(name = "account_id") + long accountId; + + @Column(name = "domain_id") + long domainId; + @Column(name = "display", updatable = true, nullable = false) protected boolean display = true; @@ -66,7 +72,7 @@ public SnapshotPolicyVO() { this.uuid = UUID.randomUUID().toString(); } - public SnapshotPolicyVO(long volumeId, String schedule, String timezone, IntervalType intvType, int maxSnaps, boolean display) { + public SnapshotPolicyVO(long volumeId, String schedule, String timezone, IntervalType intvType, int maxSnaps, long accountId, long domainId, boolean display) { this.volumeId = volumeId; this.schedule = schedule; this.timezone = timezone; @@ -75,6 +81,8 @@ public SnapshotPolicyVO(long volumeId, String schedule, String timezone, Interva this.active = true; this.display = display; this.uuid = UUID.randomUUID().toString(); + this.accountId = accountId; + this.domainId = domainId; } @Override @@ -160,4 +168,32 @@ public boolean isDisplay() { public void setDisplay(boolean display) { this.display = display; } + + @Override + public long getAccountId() { + return accountId; + } + + public void setAccountId(long accountId) { + this.accountId = accountId; + } + + @Override + public long getDomainId() { + return domainId; + } + + public void setDomainId(long domainId) { + this.domainId = domainId; + } + + @Override + public Class getEntityType() { + return SnapshotPolicy.class; + } + + @Override + public String getName() { + return null; + } } 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 4a504333344f..be7ba2843321 100644 --- a/engine/schema/src/main/java/com/cloud/storage/SnapshotVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/SnapshotVO.java @@ -248,6 +248,10 @@ public Date getRemoved() { return removed; } + public void setRemoved(Date removed) { + this.removed = removed; + } + @Override public State getState() { return state; diff --git a/engine/schema/src/main/java/com/cloud/storage/StoragePoolHostVO.java b/engine/schema/src/main/java/com/cloud/storage/StoragePoolHostVO.java index 53c08322e0cf..73a11b02c050 100644 --- a/engine/schema/src/main/java/com/cloud/storage/StoragePoolHostVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/StoragePoolHostVO.java @@ -28,6 +28,7 @@ import javax.persistence.TemporalType; import com.cloud.utils.db.GenericDaoBase; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; /** * Join table for storage pools and hosts @@ -100,4 +101,9 @@ public void setLocalPath(String localPath) { this.localPath = localPath; } + @Override + public String toString() { + return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "hostId", "poolId"); + } + } diff --git a/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java b/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java index 653be54a9109..3815125b3487 100644 --- a/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java @@ -182,6 +182,12 @@ public class VolumeVO implements Volume { @Column(name = "passphrase_id") private Long passphraseId; + @Column(name = "kms_key_id") + private Long kmsKeyId; + + @Column(name = "kms_wrapped_key_id") + private Long kmsWrappedKeyId; + @Column(name = "encrypt_format") private String encryptFormat; @@ -683,6 +689,14 @@ public void setExternalUuid(String externalUuid) { public void setPassphraseId(Long id) { this.passphraseId = id; } + public Long getKmsKeyId() { return kmsKeyId; } + + public void setKmsKeyId(Long id) { this.kmsKeyId = id; } + + public Long getKmsWrappedKeyId() { return kmsWrappedKeyId; } + + public void setKmsWrappedKeyId(Long id) { this.kmsWrappedKeyId = id; } + public String getEncryptFormat() { return encryptFormat; } public void setEncryptFormat(String encryptFormat) { this.encryptFormat = encryptFormat; } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java index f726bca3c5d6..ea97f663768f 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java @@ -30,7 +30,9 @@ public interface DiskOfferingDao extends GenericDao { List listAllBySizeAndProvisioningType(long size, Storage.ProvisioningType provisioningType); - List findCustomDiskOfferings(); + List listCustomDiskOfferings(); + List listByStorageTag(String tag); + List listAllActiveAndNonComputeDiskOfferings(); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDaoImpl.java index 93e747662775..44e6551da17d 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDaoImpl.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.storage.dao; +import static org.apache.cloudstack.query.QueryService.SortKeyAscending; + import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -26,12 +28,14 @@ import javax.inject.Inject; import javax.persistence.EntityExistsException; +import com.cloud.offering.DiskOffering; import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao; import org.springframework.stereotype.Component; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.Storage; import com.cloud.utils.db.Attribute; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -45,6 +49,8 @@ public class DiskOfferingDaoImpl extends GenericDaoBase im protected DiskOfferingDetailsDao detailsDao; protected final SearchBuilder UniqueNameSearch; + protected final SearchBuilder ActiveAndNonComputeSearch; + private final String SizeDiskOfferingSearch = "SELECT * FROM disk_offering WHERE " + "disk_size = ? AND provisioning_type = ? AND removed IS NULL"; @@ -56,6 +62,11 @@ protected DiskOfferingDaoImpl() { UniqueNameSearch.and("name", UniqueNameSearch.entity().getUniqueName(), SearchCriteria.Op.EQ); UniqueNameSearch.done(); + ActiveAndNonComputeSearch = createSearchBuilder(); + ActiveAndNonComputeSearch.and("state", ActiveAndNonComputeSearch.entity().getState(), SearchCriteria.Op.EQ); + ActiveAndNonComputeSearch.and("computeOnly", ActiveAndNonComputeSearch.entity().isComputeOnly(), SearchCriteria.Op.EQ); + ActiveAndNonComputeSearch.done(); + _computeOnlyAttr = _allAttributes.get("computeOnly"); } @@ -130,13 +141,14 @@ public List listAllBySizeAndProvisioningType(long size, Storage. } @Override - public List findCustomDiskOfferings() { + public List listCustomDiskOfferings() { SearchBuilder sb = createSearchBuilder(); sb.and("customized", sb.entity().isCustomized(), SearchCriteria.Op.EQ); sb.done(); SearchCriteria sc = sb.create(); sc.setParameters("customized", true); - return listBy(sc); + Filter searchFilter = new Filter(DiskOfferingVO.class, "sortKey", SortKeyAscending.value()); + return listBy(sc, searchFilter); } @Override @@ -164,4 +176,12 @@ public List listByStorageTag(String tag) { sc.setParameters("tagEndLike", "%," + tag); return listBy(sc); } + + @Override + public List listAllActiveAndNonComputeDiskOfferings() { + SearchCriteria sc = ActiveAndNonComputeSearch.create(); + sc.setParameters("state", DiskOffering.State.Active); + sc.setParameters("computeOnly", false); + return listBy(sc); + } } 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 1a2b098c40a7..f24f3f3b67c4 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 @@ -35,7 +35,7 @@ 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); + Pair, Integer> listGuestOSByCriteria(Long startIndex, Long pageSize, List ids, 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 881be207c1aa..09f8772fb59c 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 @@ -125,12 +125,12 @@ public List listByDisplayName(String displayName) { return listBy(sc); } - public Pair, Integer> listGuestOSByCriteria(Long startIndex, Long pageSize, Long id, Long osCategoryId, String description, String keyword, Boolean forDisplay) { + public Pair, Integer> listGuestOSByCriteria(Long startIndex, Long pageSize, List ids, Long osCategoryId, String description, String keyword, Boolean forDisplay) { final Filter searchFilter = new Filter(GuestOSVO.class, "displayName", true, startIndex, pageSize); final SearchCriteria sc = createSearchCriteria(); - if (id != null) { - sc.addAnd("id", SearchCriteria.Op.EQ, id); + if (CollectionUtils.isNotEmpty(ids)) { + sc.addAnd("id", SearchCriteria.Op.IN, ids.toArray()); } if (osCategoryId != null) { diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/LaunchPermissionDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/LaunchPermissionDaoImpl.java index b4fdb4b6394e..f154692c6c87 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/LaunchPermissionDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/LaunchPermissionDaoImpl.java @@ -143,7 +143,7 @@ public List listPermittedTemplates(long accountId) { permittedTemplates.add(template); } } catch (Exception e) { - logger.warn("Error listing permitted templates", e); + logger.warn("Error listing permitted Templates", e); } return permittedTemplates; } 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 3cda7d42760e..f851f158331e 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 @@ -47,6 +47,8 @@ public interface SnapshotDao extends GenericDao, StateDao listAllByStatus(Snapshot.State... status); + List listAllByStatusIncludingRemoved(Snapshot.State... status); + void updateVolumeIds(long oldVolId, long newVolId); List listByStatusNotIn(long volumeId, Snapshot.State... status); 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 c479a386d794..a5596ed840cc 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 @@ -56,10 +56,18 @@ public class SnapshotDaoImpl extends GenericDaoBase implements // TODO: we should remove these direct sqls 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 TYPE = "type"; private static final String STATUS = "status"; + private static final String VERSION = "version"; + private static final String ACCOUNT_ID = "accountId"; + private static final String REMOVED = "removed"; + private static final String NOT_TYPE = "notType"; + private static final String ID = "id"; + private static final String INSTANCE_ID = "instanceId"; + private static final String STATE = "state"; + private static final String INSTANCE_VOLUMES = "instanceVolumes"; + private static final String INSTANCE_SNAPSHOTS = "instanceSnapshots"; private SearchBuilder snapshotIdsSearch; private SearchBuilder VolumeIdSearch; @@ -83,9 +91,9 @@ public class SnapshotDaoImpl extends GenericDaoBase implements @Override public List listByVolumeIdTypeNotDestroyed(long volumeId, Type type) { SearchCriteria sc = VolumeIdTypeNotDestroyedSearch.create(); - sc.setParameters("volumeId", volumeId); - sc.setParameters("type", type.ordinal()); - sc.setParameters("status", State.Destroyed); + sc.setParameters(VOLUME_ID, volumeId); + sc.setParameters(TYPE, type.ordinal()); + sc.setParameters(STATUS, State.Destroyed); return listBy(sc, null); } @@ -102,28 +110,28 @@ public List listByVolumeId(long volumeId) { @Override public List listByVolumeId(Filter filter, long volumeId) { SearchCriteria sc = VolumeIdSearch.create(); - sc.setParameters("volumeId", volumeId); + sc.setParameters(VOLUME_ID, volumeId); return listBy(sc, filter); } @Override public List listByVolumeIdIncludingRemoved(long volumeId) { SearchCriteria sc = VolumeIdSearch.create(); - sc.setParameters("volumeId", volumeId); + sc.setParameters(VOLUME_ID, volumeId); return listIncludingRemovedBy(sc, null); } public List listByVolumeIdType(Filter filter, long volumeId, Type type) { SearchCriteria sc = VolumeIdTypeSearch.create(); - sc.setParameters("volumeId", volumeId); - sc.setParameters("type", type.ordinal()); + sc.setParameters(VOLUME_ID, volumeId); + sc.setParameters(TYPE, type.ordinal()); return listBy(sc, filter); } public List listByVolumeIdVersion(Filter filter, long volumeId, String version) { SearchCriteria sc = VolumeIdVersionSearch.create(); - sc.setParameters("volumeId", volumeId); - sc.setParameters("version", version); + sc.setParameters(VOLUME_ID, volumeId); + sc.setParameters(VERSION, version); return listBy(sc, filter); } @@ -133,60 +141,61 @@ public SnapshotDaoImpl() { @PostConstruct protected void init() { VolumeIdSearch = createSearchBuilder(); - VolumeIdSearch.and("volumeId", VolumeIdSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); + VolumeIdSearch.and(VOLUME_ID, VolumeIdSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); VolumeIdSearch.done(); VolumeIdTypeSearch = createSearchBuilder(); - VolumeIdTypeSearch.and("volumeId", VolumeIdTypeSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); - VolumeIdTypeSearch.and("type", VolumeIdTypeSearch.entity().getSnapshotType(), SearchCriteria.Op.EQ); + VolumeIdTypeSearch.and(VOLUME_ID, VolumeIdTypeSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); + VolumeIdTypeSearch.and(TYPE, VolumeIdTypeSearch.entity().getSnapshotType(), SearchCriteria.Op.EQ); VolumeIdTypeSearch.done(); VolumeIdTypeNotDestroyedSearch = createSearchBuilder(); - VolumeIdTypeNotDestroyedSearch.and("volumeId", VolumeIdTypeNotDestroyedSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); - VolumeIdTypeNotDestroyedSearch.and("type", VolumeIdTypeNotDestroyedSearch.entity().getSnapshotType(), SearchCriteria.Op.EQ); - VolumeIdTypeNotDestroyedSearch.and("status", VolumeIdTypeNotDestroyedSearch.entity().getState(), SearchCriteria.Op.NEQ); + VolumeIdTypeNotDestroyedSearch.and(VOLUME_ID, VolumeIdTypeNotDestroyedSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); + VolumeIdTypeNotDestroyedSearch.and(TYPE, VolumeIdTypeNotDestroyedSearch.entity().getSnapshotType(), SearchCriteria.Op.EQ); + VolumeIdTypeNotDestroyedSearch.and(STATUS, VolumeIdTypeNotDestroyedSearch.entity().getState(), SearchCriteria.Op.NEQ); VolumeIdTypeNotDestroyedSearch.done(); VolumeIdVersionSearch = createSearchBuilder(); - VolumeIdVersionSearch.and("volumeId", VolumeIdVersionSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); - VolumeIdVersionSearch.and("version", VolumeIdVersionSearch.entity().getVersion(), SearchCriteria.Op.EQ); + VolumeIdVersionSearch.and(VOLUME_ID, VolumeIdVersionSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); + VolumeIdVersionSearch.and(VERSION, VolumeIdVersionSearch.entity().getVersion(), SearchCriteria.Op.EQ); VolumeIdVersionSearch.done(); AccountIdSearch = createSearchBuilder(); - AccountIdSearch.and("accountId", AccountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + AccountIdSearch.and(ACCOUNT_ID, AccountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ); AccountIdSearch.done(); StatusSearch = createSearchBuilder(); - StatusSearch.and("volumeId", StatusSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); - StatusSearch.and("status", StatusSearch.entity().getState(), SearchCriteria.Op.IN); + StatusSearch.and(VOLUME_ID, StatusSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); + StatusSearch.and(STATUS, StatusSearch.entity().getState(), SearchCriteria.Op.IN); StatusSearch.done(); notInStatusSearch = createSearchBuilder(); - notInStatusSearch.and("volumeId", notInStatusSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); - notInStatusSearch.and("status", notInStatusSearch.entity().getState(), SearchCriteria.Op.NOTIN); + notInStatusSearch.and(VOLUME_ID, notInStatusSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); + notInStatusSearch.and(STATUS, notInStatusSearch.entity().getState(), SearchCriteria.Op.NOTIN); notInStatusSearch.done(); CountSnapshotsByAccount = createSearchBuilder(Long.class); CountSnapshotsByAccount.select(null, Func.COUNT, null); - CountSnapshotsByAccount.and("account", CountSnapshotsByAccount.entity().getAccountId(), SearchCriteria.Op.EQ); - CountSnapshotsByAccount.and("status", CountSnapshotsByAccount.entity().getState(), SearchCriteria.Op.NIN); - CountSnapshotsByAccount.and("removed", CountSnapshotsByAccount.entity().getRemoved(), SearchCriteria.Op.NULL); + CountSnapshotsByAccount.and(ACCOUNT_ID, CountSnapshotsByAccount.entity().getAccountId(), SearchCriteria.Op.EQ); + CountSnapshotsByAccount.and(STATUS, CountSnapshotsByAccount.entity().getState(), SearchCriteria.Op.NIN); + CountSnapshotsByAccount.and("snapshotTypeNEQ", CountSnapshotsByAccount.entity().getSnapshotType(), SearchCriteria.Op.NIN); + CountSnapshotsByAccount.and(REMOVED, CountSnapshotsByAccount.entity().getRemoved(), SearchCriteria.Op.NULL); CountSnapshotsByAccount.done(); InstanceIdSearch = createSearchBuilder(); - InstanceIdSearch.and("status", InstanceIdSearch.entity().getState(), SearchCriteria.Op.IN); + InstanceIdSearch.and(STATUS, InstanceIdSearch.entity().getState(), SearchCriteria.Op.IN); snapshotIdsSearch = createSearchBuilder(); - snapshotIdsSearch.and("id", snapshotIdsSearch.entity().getId(), SearchCriteria.Op.IN); + snapshotIdsSearch.and(ID, snapshotIdsSearch.entity().getId(), SearchCriteria.Op.IN); SearchBuilder instanceSearch = _instanceDao.createSearchBuilder(); - instanceSearch.and("instanceId", instanceSearch.entity().getId(), SearchCriteria.Op.EQ); + instanceSearch.and(INSTANCE_ID, instanceSearch.entity().getId(), SearchCriteria.Op.EQ); SearchBuilder volumeSearch = _volumeDao.createSearchBuilder(); - volumeSearch.and("state", volumeSearch.entity().getState(), SearchCriteria.Op.EQ); - volumeSearch.join("instanceVolumes", instanceSearch, instanceSearch.entity().getId(), volumeSearch.entity().getInstanceId(), JoinType.INNER); + volumeSearch.and(STATE, volumeSearch.entity().getState(), SearchCriteria.Op.EQ); + volumeSearch.join(INSTANCE_VOLUMES, instanceSearch, instanceSearch.entity().getId(), volumeSearch.entity().getInstanceId(), JoinType.INNER); - InstanceIdSearch.join("instanceSnapshots", volumeSearch, volumeSearch.entity().getId(), InstanceIdSearch.entity().getVolumeId(), JoinType.INNER); + InstanceIdSearch.join(INSTANCE_SNAPSHOTS, volumeSearch, volumeSearch.entity().getId(), InstanceIdSearch.entity().getVolumeId(), JoinType.INNER); InstanceIdSearch.done(); volumeIdAndTypeNotInSearch = createSearchBuilder(); @@ -218,8 +227,9 @@ public long getLastSnapshot(long volumeId, DataStoreRole role) { @Override public Long countSnapshotsForAccount(long accountId) { SearchCriteria sc = CountSnapshotsByAccount.create(); - sc.setParameters("account", accountId); - sc.setParameters("status", State.Error, State.Destroyed); + sc.setParameters(ACCOUNT_ID, accountId); + sc.setParameters(STATUS, State.Error, State.Destroyed); + sc.setParameters("snapshotTypeNEQ", Snapshot.Type.GROUP.ordinal()); return customSearch(sc, null).get(0); } @@ -228,19 +238,19 @@ public List listByInstanceId(long instanceId, Snapshot.State... stat SearchCriteria sc = InstanceIdSearch.create(); if (status != null && status.length != 0) { - sc.setParameters("status", (Object[])status); + sc.setParameters(STATUS, (Object[])status); } - sc.setJoinParameters("instanceSnapshots", "state", Volume.State.Ready); - sc.setJoinParameters("instanceVolumes", "instanceId", instanceId); + sc.setJoinParameters(INSTANCE_SNAPSHOTS, STATE, Volume.State.Ready); + sc.setJoinParameters(INSTANCE_VOLUMES, INSTANCE_ID, instanceId); return listBy(sc, null); } @Override public List listByStatus(long volumeId, Snapshot.State... status) { SearchCriteria sc = StatusSearch.create(); - sc.setParameters("volumeId", volumeId); - sc.setParameters("status", (Object[])status); + sc.setParameters(VOLUME_ID, volumeId); + sc.setParameters(STATUS, (Object[])status); return listBy(sc, null); } @@ -261,14 +271,21 @@ public boolean remove(Long id) { @Override public List listAllByStatus(Snapshot.State... status) { SearchCriteria sc = StatusSearch.create(); - sc.setParameters("status", (Object[])status); + sc.setParameters(STATUS, (Object[])status); return listBy(sc, null); } + @Override + public List listAllByStatusIncludingRemoved(Snapshot.State... status) { + SearchCriteria sc = StatusSearch.create(); + sc.setParameters("status", (Object[])status); + return listIncludingRemovedBy(sc, null); + } + @Override public List listByIds(Object... ids) { SearchCriteria sc = snapshotIdsSearch.create(); - sc.setParameters("id", ids); + sc.setParameters(ID, ids); return listBy(sc, null); } @@ -286,7 +303,7 @@ public boolean updateState(State currentState, Event event, State nextState, Sna @Override public void updateVolumeIds(long oldVolId, long newVolId) { SearchCriteria sc = VolumeIdSearch.create(); - sc.setParameters("volumeId", oldVolId); + sc.setParameters(VOLUME_ID, oldVolId); SnapshotVO snapshot = createForUpdate(); snapshot.setVolumeId(newVolId); UpdateBuilder ub = getUpdateBuilder(snapshot); @@ -296,8 +313,8 @@ public void updateVolumeIds(long oldVolId, long newVolId) { @Override public List listByStatusNotIn(long volumeId, Snapshot.State... status) { SearchCriteria sc = this.notInStatusSearch.create(); - sc.setParameters("volumeId", volumeId); - sc.setParameters("status", (Object[]) status); + sc.setParameters(VOLUME_ID, volumeId); + sc.setParameters(STATUS, (Object[]) status); return listBy(sc, null); } 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 6785c3653290..aec06d6d0003 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 @@ -94,7 +94,7 @@ public interface VMTemplateDao extends GenericDao, StateDao< List listByParentTemplatetId(long parentTemplatetId); - VMTemplateVO findLatestTemplateByName(String name, CPU.CPUArch arch); + VMTemplateVO findLatestTemplateByName(String name, HypervisorType hypervisorType, CPU.CPUArch arch); List findTemplatesLinkedToUserdata(long userdataId); @@ -103,4 +103,9 @@ public interface VMTemplateDao extends GenericDao, StateDao< List listIdsByTemplateTag(String tag); List listIdsByExtensionId(long extensionId); + + VMTemplateVO findActiveSystemTemplateByHypervisorArchAndUrlPath(HypervisorType hypervisorType, + CPU.CPUArch arch, String urlPathSuffix); + + VMTemplateVO findByAccountAndName(Long accountId, String templateName); } 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 08b82cbb45bc..8c6e3fe0983f 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 @@ -100,7 +100,6 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem private SearchBuilder PublicIsoSearch; private SearchBuilder UserIsoSearch; private GenericSearchBuilder CountTemplatesByAccount; - // private SearchBuilder updateStateSearch; private SearchBuilder AllFieldsSearch; protected SearchBuilder ParentTemplateIdSearch; private SearchBuilder InactiveUnremovedTmpltSearch; @@ -246,13 +245,17 @@ public List listReadyTemplates() { @Override - public VMTemplateVO findLatestTemplateByName(String name, CPU.CPUArch arch) { + public VMTemplateVO findLatestTemplateByName(String name, HypervisorType hypervisorType, CPU.CPUArch arch) { SearchBuilder sb = createSearchBuilder(); sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ); + sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ); sb.and("arch", sb.entity().getArch(), SearchCriteria.Op.EQ); sb.done(); SearchCriteria sc = sb.create(); sc.setParameters("name", name); + if (hypervisorType != null) { + sc.setParameters("hypervisorType", hypervisorType); + } if (arch != null) { sc.setParameters("arch", arch); } @@ -315,7 +318,7 @@ public boolean configure(String name, Map params) throws Configu consoleProxyTmpltName = "routing"; } if (logger.isDebugEnabled()) { - logger.debug("Use console proxy template : " + consoleProxyTmpltName); + logger.debug("Use console proxy Template : " + consoleProxyTmpltName); } UniqueNameSearch = createSearchBuilder(); @@ -404,12 +407,6 @@ public boolean configure(String name, Map params) throws Configu CountTemplatesByAccount.and("state", CountTemplatesByAccount.entity().getState(), SearchCriteria.Op.EQ); CountTemplatesByAccount.done(); - // updateStateSearch = this.createSearchBuilder(); - // updateStateSearch.and("id", updateStateSearch.entity().getId(), Op.EQ); - // updateStateSearch.and("state", updateStateSearch.entity().getState(), Op.EQ); - // updateStateSearch.and("updatedCount", updateStateSearch.entity().getUpdatedCount(), Op.EQ); - // updateStateSearch.done(); - AllFieldsSearch = createSearchBuilder(); AllFieldsSearch.and("state", AllFieldsSearch.entity().getState(), SearchCriteria.Op.EQ); AllFieldsSearch.and("accountId", AllFieldsSearch.entity().getAccountId(), SearchCriteria.Op.EQ); @@ -460,7 +457,7 @@ public void saveDetails(VMTemplateVO tmpl) { if (detailsStr == null) { return; } - List details = new ArrayList(); + List details = new ArrayList<>(); for (String key : detailsStr.keySet()) { VMTemplateDetailVO detail = new VMTemplateDetailVO(tmpl.getId(), key, detailsStr.get(key), true); details.add(detail); @@ -478,11 +475,11 @@ public long addTemplateToZone(VMTemplateVO tmplt, long zoneId) { VMTemplateVO tmplt2 = findById(tmplt.getId()); if (tmplt2 == null) { if (persist(tmplt) == null) { - throw new CloudRuntimeException("Failed to persist the template " + tmplt); + throw new CloudRuntimeException("Failed to persist the Template " + tmplt); } if (tmplt.getDetails() != null) { - List details = new ArrayList(); + List details = new ArrayList<>(); for (String key : tmplt.getDetails().keySet()) { details.add(new VMTemplateDetailVO(tmplt.getId(), key, tmplt.getDetails().get(key), true)); } @@ -857,6 +854,37 @@ public List listIdsByExtensionId(long extensionId) { return customSearch(sc, null); } + @Override + public VMTemplateVO findActiveSystemTemplateByHypervisorArchAndUrlPath(HypervisorType hypervisorType, + CPU.CPUArch arch, String urlPathSuffix) { + if (StringUtils.isBlank(urlPathSuffix)) { + return null; + } + SearchBuilder sb = createSearchBuilder(); + sb.and("templateType", sb.entity().getTemplateType(), SearchCriteria.Op.EQ); + sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ); + sb.and("arch", sb.entity().getArch(), SearchCriteria.Op.EQ); + sb.and("urlPathSuffix", sb.entity().getUrl(), SearchCriteria.Op.LIKE); + sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("templateType", TemplateType.SYSTEM); + if (hypervisorType != null) { + sc.setParameters("hypervisorType", hypervisorType); + } + if (arch != null) { + sc.setParameters("arch", arch); + } + sc.setParameters("urlPathSuffix", "%" + urlPathSuffix); + sc.setParameters("state", VirtualMachineTemplate.State.Active); + Filter filter = new Filter(VMTemplateVO.class, "id", false, null, 1L); + List templates = listBy(sc, filter); + if (CollectionUtils.isNotEmpty(templates)) { + return templates.get(0); + } + return null; + } + @Override public boolean updateState( com.cloud.template.VirtualMachineTemplate.State currentState, @@ -912,9 +940,17 @@ public boolean updateState( .append("; updatedTime=") .append(oldUpdatedTime); } else { - logger.debug("Unable to update template: id=" + vo.getId() + ", as no such template exists in the database anymore"); + logger.debug("Unable to update Template: id=" + vo.getId() + ", as no such template exists in the database anymore"); } } return rows > 0; } + + @Override + public VMTemplateVO findByAccountAndName(Long accountId, String templateName) { + SearchCriteria sc = NameAccountIdSearch.create(); + sc.setParameters("name", templateName); + sc.setParameters("accountId", accountId); + return findOneBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDao.java index a3ce03a74c34..a208590e23a3 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDao.java @@ -35,6 +35,8 @@ public interface VMTemplatePoolDao extends GenericDao listByPoolIdAndState(long poolId, ObjectInDataStoreStateMachine.State state); + List listByPoolIdsAndTemplate(List poolIds, Long templateId); + List listByTemplateStatus(long templateId, VMTemplateStoragePoolVO.Status downloadState); List listByTemplateStatus(long templateId, VMTemplateStoragePoolVO.Status downloadState, long poolId); diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDaoImpl.java index 5a2ec1163fb0..5dfc138d8e1b 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDaoImpl.java @@ -150,6 +150,16 @@ public VMTemplateStoragePoolVO findByPoolTemplate(long poolId, long templateId, return findOneIncludingRemovedBy(sc); } + @Override + public List listByPoolIdsAndTemplate(List poolIds, Long templateId) { + SearchCriteria sc = PoolTemplateSearch.create(); + if (CollectionUtils.isNotEmpty(poolIds)) { + sc.setParameters("pool_id", poolIds.toArray()); + } + sc.setParameters("template_id", templateId); + return listBy(sc); + } + @Override public List listByTemplateStatus(long templateId, VMTemplateStoragePoolVO.Status downloadState) { SearchCriteria sc = TemplateStatusSearch.create(); 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 0f47c9e71555..4cd9a8e23bfc 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 @@ -46,9 +46,11 @@ public interface VolumeDao extends GenericDao, StateDao findByInstanceAndType(long id, Volume.Type vType); + List findByInstanceAndNotStates(long id, Volume.State...states); + List findIncludingRemovedByInstanceAndType(long id, Volume.Type vType); - List findByInstanceIdAndPoolId(long instanceId, long poolId); + List findNonDestroyedVolumesByInstanceIdAndPoolId(long instanceId, long poolId); List findByInstanceIdDestroyed(long vmId); @@ -70,11 +72,11 @@ public interface VolumeDao extends GenericDao, StateDao findCreatedByInstance(long id); - List findByPoolId(long poolId); + List findNonDestroyedVolumesByPoolId(long poolId); VolumeVO findByPoolIdName(long poolId, String name); - List findByPoolId(long poolId, Volume.Type volumeType); + List findNonDestroyedVolumesByPoolId(long poolId, Volume.Type volumeType); List findByPoolIdAndState(long poolid, Volume.State state); @@ -109,6 +111,17 @@ public interface VolumeDao extends GenericDao, StateDao listVolumesByPassphraseId(long passphraseId); + /** + * List volumes with passphrase_id for migration to KMS + * + * @param zoneId Zone ID (required) + * @param accountId Account ID filter (optional, null for all accounts) + * @param domainId Domain ID filter (optional, null for all domains) + * @param limit Maximum number of volumes to return + * @return list of volumes that need migration + */ + Pair, Integer> listVolumesForKMSMigration(Long zoneId, Long accountId, Long domainId, Integer limit); + /** * Gets the Total Primary Storage space allocated for an account * @@ -163,5 +176,19 @@ public interface VolumeDao extends GenericDao, StateDao findByKmsWrappedKeyId(Long kmsWrappedKeyId); } 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 f8b6bb3ed680..1d5ff5e93402 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 @@ -29,6 +29,7 @@ import org.apache.cloudstack.reservation.ReservationVO; import org.apache.cloudstack.reservation.dao.ReservationDao; +import org.apache.cloudstack.kms.dao.KMSWrappedKeyDao; import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; @@ -74,15 +75,20 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol private final SearchBuilder storeAndInstallPathSearch; private final SearchBuilder volumeIdSearch; protected GenericSearchBuilder CountByAccount; + protected final SearchBuilder ExternalUuidSearch; protected GenericSearchBuilder primaryStorageSearch; protected GenericSearchBuilder primaryStorageSearch2; protected GenericSearchBuilder secondaryStorageSearch; private final SearchBuilder poolAndPathSearch; + final GenericSearchBuilder CountByOfferingId; + private final SearchBuilder kmsMigrationSearch; @Inject ReservationDao reservationDao; @Inject ResourceTagDao tagsDao; + @Inject + KMSWrappedKeyDao kmsWrappedKeyDao; // need to account for zone-wide primary storage where storage_pool has // null-value pod and cluster, where hypervisor information is stored in @@ -135,7 +141,7 @@ public List findByInstanceAndDeviceId(long instanceId, long deviceId) } @Override - public List findByPoolId(long poolId) { + public List findNonDestroyedVolumesByPoolId(long poolId) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("poolId", poolId); sc.setParameters("notDestroyed", Volume.State.Destroy, Volume.State.Expunged); @@ -144,7 +150,7 @@ public List findByPoolId(long poolId) { } @Override - public List findByInstanceIdAndPoolId(long instanceId, long poolId) { + public List findNonDestroyedVolumesByInstanceIdAndPoolId(long instanceId, long poolId) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("instanceId", instanceId); sc.setParameters("poolId", poolId); @@ -161,7 +167,7 @@ public VolumeVO findByPoolIdName(long poolId, String name) { } @Override - public List findByPoolId(long poolId, Volume.Type volumeType) { + public List findNonDestroyedVolumesByPoolId(long poolId, Volume.Type volumeType) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("poolId", poolId); sc.setParameters("notDestroyed", Volume.State.Destroy, Volume.State.Expunged); @@ -206,6 +212,17 @@ public List findByInstanceAndType(long id, Type vType) { return listBy(sc); } + @Override + public List findByInstanceAndNotStates(long id, Volume.State...states) { + SearchBuilder sb = createSearchBuilder(); + sb.and("instanceId", sb.entity().getInstanceId(), Op.EQ); + sb.and("state", sb.entity().getState(), Op.NIN); + SearchCriteria sc = sb.create(); + sc.setParameters("instanceId", id); + sc.setParameters("state", (Object[]) states); + return listBy(sc); + } + @Override public List findIncludingRemovedByInstanceAndType(long id, Type vType) { SearchCriteria sc = AllFieldsSearch.create(); @@ -382,7 +399,7 @@ public ImageFormat getImageFormat(Long volumeId) { public VolumeDaoImpl() { AllFieldsSearch = createSearchBuilder(); - AllFieldsSearch.and("state", AllFieldsSearch.entity().getState(), Op.EQ); + AllFieldsSearch.and("state", AllFieldsSearch.entity().getState(), Op.IN); AllFieldsSearch.and("accountId", AllFieldsSearch.entity().getAccountId(), Op.EQ); AllFieldsSearch.and("dcId", AllFieldsSearch.entity().getDataCenterId(), Op.EQ); AllFieldsSearch.and("pod", AllFieldsSearch.entity().getPodId(), Op.EQ); @@ -399,6 +416,8 @@ public VolumeDaoImpl() { 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.and("kmsKeyId", AllFieldsSearch.entity().getKmsKeyId(), Op.EQ); + AllFieldsSearch.and("kmsWrappedKeyId", AllFieldsSearch.entity().getKmsWrappedKeyId(), Op.EQ); AllFieldsSearch.done(); RootDiskStateSearch = createSearchBuilder(); @@ -458,6 +477,10 @@ public VolumeDaoImpl() { CountByAccount.and("idNIN", CountByAccount.entity().getId(), Op.NIN); CountByAccount.done(); + ExternalUuidSearch = createSearchBuilder(); + ExternalUuidSearch.and("externalUuid", ExternalUuidSearch.entity().getExternalUuid(), Op.EQ); + ExternalUuidSearch.done(); + primaryStorageSearch = createSearchBuilder(SumCount.class); primaryStorageSearch.select("sum", Func.SUM, primaryStorageSearch.entity().getSize()); primaryStorageSearch.and("accountId", primaryStorageSearch.entity().getAccountId(), Op.EQ); @@ -506,6 +529,18 @@ public VolumeDaoImpl() { poolAndPathSearch.and("poolId", poolAndPathSearch.entity().getPoolId(), Op.EQ); poolAndPathSearch.and("path", poolAndPathSearch.entity().getPath(), Op.EQ); poolAndPathSearch.done(); + + CountByOfferingId = createSearchBuilder(Integer.class); + CountByOfferingId.select(null, Func.COUNT, CountByOfferingId.entity().getId()); + CountByOfferingId.and("diskOfferingId", CountByOfferingId.entity().getDiskOfferingId(), Op.EQ); + CountByOfferingId.done(); + + kmsMigrationSearch = createSearchBuilder(); + kmsMigrationSearch.and("passphraseId", kmsMigrationSearch.entity().getPassphraseId(), Op.NNULL); + kmsMigrationSearch.and("zoneId", kmsMigrationSearch.entity().getDataCenterId(), Op.EQ); + kmsMigrationSearch.and("accountId", kmsMigrationSearch.entity().getAccountId(), Op.EQ); + kmsMigrationSearch.and("domainId", kmsMigrationSearch.entity().getDomainId(), Op.EQ); + kmsMigrationSearch.done(); } @Override @@ -575,17 +610,16 @@ public long secondaryStorageUsedForAccount(long accountId) { @Override public List listVolumesToBeDestroyed() { - SearchCriteria sc = AllFieldsSearch.create(); - sc.setParameters("state", Volume.State.Destroy); - - return listBy(sc); + return listVolumesToBeDestroyed(null); } @Override public List listVolumesToBeDestroyed(Date date) { SearchCriteria sc = AllFieldsSearch.create(); - sc.setParameters("state", Volume.State.Destroy); - sc.setParameters("updateTime", date); + sc.setParameters("state", Volume.State.Destroy, Volume.State.Expunging); + if (date != null) { + sc.setParameters("updateTime", date); + } return listBy(sc); } @@ -727,6 +761,21 @@ public List listVolumesByPassphraseId(long passphraseId) { return listBy(sc); } + @Override + public Pair, Integer> listVolumesForKMSMigration(Long zoneId, Long accountId, Long domainId, Integer limit) { + SearchCriteria sc = kmsMigrationSearch.create(); + + Filter filter = new Filter(limit); + sc.setParameters("zoneId", zoneId); + if (accountId != null) { + sc.setParameters("accountId", accountId); + } + if (domainId != null) { + sc.setParameters("domainId", domainId); + } + return searchAndCount(sc, filter); + } + @Override @DB public boolean remove(Long id) { @@ -735,6 +784,17 @@ public boolean remove(Long id) { logger.debug(String.format("Removing volume %s from DB", id)); VolumeVO entry = findById(id); if (entry != null) { + // Clean up KMS wrapped key if volume was encrypted with KMS + if (entry.getKmsWrappedKeyId() != null) { + try { + kmsWrappedKeyDao.remove(entry.getKmsWrappedKeyId()); + logger.debug("Removed KMS wrapped key [id={}] for volume [id={}, uuid={}]", + entry.getKmsWrappedKeyId(), id, entry.getUuid()); + } catch (Exception e) { + logger.warn("Failed to remove KMS wrapped key [id={}] for volume [id={}, uuid={}]: {}", + entry.getKmsWrappedKeyId(), id, entry.getUuid(), e.getMessage(), e); + } + } tagsDao.removeByIdAndType(id, ResourceObjectType.Volume); } boolean result = super.remove(id); @@ -822,6 +882,7 @@ public void updateAndRemoveVolume(VolumeVO volume) { if (volume.getState() != Volume.State.Destroy) { volume.setState(Volume.State.Destroy); volume.setPoolId(null); + volume.setPoolType(null); volume.setInstanceId(null); update(volume.getId(), volume); remove(volume.getId()); @@ -913,6 +974,14 @@ public VolumeVO findOneByIScsiName(String iScsiName) { return findOneIncludingRemovedBy(sc); } + @Override + public int getVolumeCountByOfferingId(long diskOfferingId) { + SearchCriteria sc = CountByOfferingId.create(); + sc.setParameters("diskOfferingId", diskOfferingId); + List results = customSearch(sc, null); + return results.get(0); + } + @Override public VolumeVO findByLastIdAndState(long lastVolumeId, State ...states) { QueryBuilder sc = QueryBuilder.create(VolumeVO.class); @@ -920,4 +989,26 @@ public VolumeVO findByLastIdAndState(long lastVolumeId, State ...states) { sc.and(sc.entity().getState(), SearchCriteria.Op.IN, (Object[]) states); return sc.find(); } + + @Override + public boolean existsWithKmsKey(long kmsKeyId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("kmsKeyId", kmsKeyId); + sc.setParameters("notDestroyed", Volume.State.Expunged, Volume.State.Destroy); + return findOneBy(sc) != null; + } + + public VolumeVO findByExternalUuid(String externalUuid) { + SearchCriteria sc = ExternalUuidSearch.create(); + sc.setParameters("externalUuid", externalUuid); + return findOneBy(sc); + } + + @Override + public List findByKmsWrappedKeyId(Long kmsWrappedKeyId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("kmsWrappedKeyId", kmsWrappedKeyId); + sc.setParameters("notDestroyed", Volume.State.Expunged); + return listBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagDao.java b/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagDao.java index bacb09b98793..5efaea40a943 100644 --- a/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagDao.java +++ b/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagDao.java @@ -20,11 +20,13 @@ import java.util.Map; import java.util.Set; +import org.apache.cloudstack.api.response.ResourceTagResponse; + import com.cloud.server.ResourceTag; import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.tags.ResourceTagVO; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDao; -import org.apache.cloudstack.api.response.ResourceTagResponse; public interface ResourceTagDao extends GenericDao { @@ -60,4 +62,13 @@ public interface ResourceTagDao extends GenericDao { void removeByResourceIdAndKey(long resourceId, ResourceObjectType resourceType, String key); List listByResourceUuid(String resourceUuid); + + List listByResourceTypeKeyPrefixAndOwners(ResourceObjectType resourceType, String key, + List accountIds, List domainIds, + Filter filter); + + ResourceTagVO findByResourceTypeKeyPrefixAndValue(ResourceObjectType resourceType, String key, String value); + + List listByResourceTypeIdAndKeyPrefix(ResourceObjectType resourceType, long resourceId, String key); + } diff --git a/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagsDaoImpl.java b/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagsDaoImpl.java index cc9d99e6ab16..22c7b7b2ee5b 100644 --- a/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagsDaoImpl.java @@ -16,19 +16,22 @@ // under the License. package com.cloud.tags.dao; -import java.util.List; -import java.util.Set; -import java.util.Map; import java.util.HashMap; import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import org.apache.cloudstack.api.response.ResourceTagResponse; +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.server.ResourceTag; import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.tags.ResourceTagVO; +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.SearchCriteria.Op; @@ -120,4 +123,62 @@ public List listByResourceUuid(String resourceUuid) { sc.setParameters("resourceUuid", resourceUuid); return listBy(sc); } + + @Override + public List listByResourceTypeKeyPrefixAndOwners(ResourceObjectType resourceType, String key, + List accountIds, List domainIds, + Filter filter) { + GenericSearchBuilder sb = createSearchBuilder(String.class); + sb.select(null, SearchCriteria.Func.DISTINCT, sb.entity().getValue()); + sb.and("resourceType", sb.entity().getResourceType(), Op.EQ); + sb.and("key", sb.entity().getKey(), Op.LIKE); + boolean accountIdsNotEmpty = CollectionUtils.isNotEmpty(accountIds); + boolean domainIdsNotEmpty = CollectionUtils.isNotEmpty(domainIds); + if (accountIdsNotEmpty || domainIdsNotEmpty) { + sb.and().op("account", sb.entity().getAccountId(), SearchCriteria.Op.IN); + sb.or("domain", sb.entity().getDomainId(), SearchCriteria.Op.IN); + sb.cp(); + } + sb.done(); + final SearchCriteria sc = sb.create(); + sc.setParameters("resourceType", resourceType); + sc.setParameters("key", key + "%"); + if (accountIdsNotEmpty) { + sc.setParameters("account", accountIds.toArray()); + } + if (domainIdsNotEmpty) { + sc.setParameters("domain", domainIds.toArray()); + } + return customSearch(sc, filter); + } + + @Override + public ResourceTagVO findByResourceTypeKeyPrefixAndValue(ResourceObjectType resourceType, String key, + String value) { + SearchBuilder sb = createSearchBuilder(); + sb.and("resourceType", sb.entity().getResourceType(), Op.EQ); + sb.and("key", sb.entity().getKey(), Op.LIKE); + sb.and("value", sb.entity().getValue(), Op.EQ); + sb.done(); + final SearchCriteria sc = sb.create(); + sc.setParameters("resourceType", resourceType); + sc.setParameters("key", key + "%"); + sc.setParameters("value", value); + return findOneBy(sc); + } + + @Override + public List listByResourceTypeIdAndKeyPrefix(ResourceObjectType resourceType, long resourceId, + String key) { + SearchBuilder sb = createSearchBuilder(); + sb.and("resourceType", sb.entity().getResourceType(), Op.EQ); + sb.and("resourceId", sb.entity().getResourceId(), Op.EQ); + sb.and("key", sb.entity().getKey(), Op.LIKE); + sb.done(); + final SearchCriteria sc = sb.create(); + sc.setParameters("resourceType", resourceType); + sc.setParameters("resourceId", resourceId); + sc.setParameters("key", key + "%"); + return listBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseCreator.java b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseCreator.java index 384826227af7..cccfbe8a0065 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseCreator.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseCreator.java @@ -99,7 +99,7 @@ private static void initDB(String dbPropsFile, String rootPassword, String[] dat String username = dbProperties.getProperty(String.format("db.%s.username", database)); String password = dbProperties.getProperty(String.format("db.%s.password", database)); String dbName = dbProperties.getProperty(String.format("db.%s.name", database)); - System.out.println(String.format("========> Initializing database=%s with host=%s port=%s username=%s password=%s", dbName, host, port, username, password)); + System.out.println(String.format("========> Initializing database=%s with host=%s port=%s username=%s password=******", dbName, host, port, username)); List queries = new ArrayList(); queries.add(String.format("drop database if exists `%s`", dbName)); 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 c211b3e97280..c3a982aa70e5 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java @@ -33,11 +33,10 @@ import javax.inject.Inject; -import com.cloud.utils.FileUtil; import org.apache.cloudstack.utils.CloudStackVersion; 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 com.cloud.upgrade.dao.DbUpgrade; import com.cloud.upgrade.dao.DbUpgradeSystemVmTemplate; @@ -89,10 +88,14 @@ 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.Upgrade42020to42030; +import com.cloud.upgrade.dao.Upgrade42030to42040; +import com.cloud.upgrade.dao.Upgrade42040to42100; import com.cloud.upgrade.dao.Upgrade42100to42200; +import com.cloud.upgrade.dao.Upgrade42200to42210; import com.cloud.upgrade.dao.Upgrade420to421; import com.cloud.upgrade.dao.Upgrade421to430; +import com.cloud.upgrade.dao.Upgrade42210to42300; import com.cloud.upgrade.dao.Upgrade430to440; import com.cloud.upgrade.dao.Upgrade431to440; import com.cloud.upgrade.dao.Upgrade432to440; @@ -121,12 +124,17 @@ import com.cloud.upgrade.dao.VersionDaoImpl; import com.cloud.upgrade.dao.VersionVO; import com.cloud.upgrade.dao.VersionVO.Step; +import com.cloud.utils.FileUtil; import com.cloud.utils.component.SystemIntegrityChecker; import com.cloud.utils.crypt.DBEncryptionUtil; import com.cloud.utils.db.GlobalLock; import com.cloud.utils.db.ScriptRunner; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.db.TransactionStatus; import com.cloud.utils.exception.CloudRuntimeException; + import com.google.common.annotations.VisibleForTesting; public class DatabaseUpgradeChecker implements SystemIntegrityChecker { @@ -234,8 +242,12 @@ public DatabaseUpgradeChecker() { .next("4.19.0.0", new Upgrade41900to41910()) .next("4.19.1.0", new Upgrade41910to42000()) .next("4.20.0.0", new Upgrade42000to42010()) - .next("4.20.1.0", new Upgrade42010to42100()) + .next("4.20.2.0", new Upgrade42020to42030()) + .next("4.20.3.0", new Upgrade42030to42040()) + .next("4.20.4.0", new Upgrade42040to42100()) .next("4.21.0.0", new Upgrade42100to42200()) + .next("4.22.0.0", new Upgrade42200to42210()) + .next("4.22.1.0", new Upgrade42210to42300()) .build(); } @@ -251,7 +263,6 @@ protected void runScript(Connection conn, InputStream file) { LOGGER.error("Unable to execute upgrade script", e); throw new CloudRuntimeException("Unable to execute upgrade script", e); } - } @VisibleForTesting @@ -313,20 +324,20 @@ protected void upgrade(CloudStackVersion dbVersion, CloudStackVersion currentVer } protected void executeProcedureScripts() { - LOGGER.info(String.format("Executing Stored Procedure scripts that are under resource directory [%s].", PROCEDURES_DIRECTORY)); + LOGGER.info("Executing Stored Procedure scripts that are under resource directory [{}].", PROCEDURES_DIRECTORY); List filesPathUnderViewsDirectory = FileUtil.getFilesPathsUnderResourceDirectory(PROCEDURES_DIRECTORY); try (TransactionLegacy txn = TransactionLegacy.open("execute-procedure-scripts")) { Connection conn = txn.getConnection(); for (String filePath : filesPathUnderViewsDirectory) { - LOGGER.debug(String.format("Executing PROCEDURE script [%s].", filePath)); + LOGGER.debug("Executing PROCEDURE script [{}].", filePath); InputStream viewScript = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath); runScript(conn, viewScript); } - LOGGER.info(String.format("Finished execution of PROCEDURE scripts that are under resource directory [%s].", PROCEDURES_DIRECTORY)); + LOGGER.info("Finished execution of PROCEDURE scripts that are under resource directory [{}].", PROCEDURES_DIRECTORY); } catch (SQLException e) { String message = String.format("Unable to execute PROCEDURE scripts due to [%s].", e.getMessage()); LOGGER.error(message, e); @@ -335,7 +346,7 @@ protected void executeProcedureScripts() { } private DbUpgrade[] executeUpgrades(CloudStackVersion dbVersion, CloudStackVersion currentVersion) { - LOGGER.info("Database upgrade must be performed from " + dbVersion + " to " + currentVersion); + LOGGER.info("Database upgrade must be performed from {} to {}", dbVersion, currentVersion); final DbUpgrade[] upgrades = calculateUpgradePath(dbVersion, currentVersion); @@ -348,8 +359,8 @@ private DbUpgrade[] executeUpgrades(CloudStackVersion dbVersion, CloudStackVersi private VersionVO executeUpgrade(DbUpgrade upgrade) { VersionVO version; - LOGGER.debug("Running upgrade " + upgrade.getClass().getSimpleName() + " to upgrade from " + upgrade.getUpgradableVersionRange()[0] + "-" + upgrade - .getUpgradableVersionRange()[1] + " to " + upgrade.getUpgradedVersion()); + LOGGER.debug("Running upgrade {} to upgrade from {}-{} to {}", upgrade.getClass().getSimpleName(), upgrade.getUpgradableVersionRange()[0], upgrade + .getUpgradableVersionRange()[1], upgrade.getUpgradedVersion()); TransactionLegacy txn = TransactionLegacy.open("Upgrade"); txn.start(); try { @@ -392,8 +403,8 @@ private void executeUpgradeCleanup(DbUpgrade upgrade, VersionVO version) { // Run the corresponding '-cleanup.sql' script txn = TransactionLegacy.open("Cleanup"); try { - LOGGER.info("Cleanup upgrade " + upgrade.getClass().getSimpleName() + " to upgrade from " + upgrade.getUpgradableVersionRange()[0] + "-" + upgrade - .getUpgradableVersionRange()[1] + " to " + upgrade.getUpgradedVersion()); + LOGGER.info("Cleanup upgrade {} to upgrade from {}-{} to {}", upgrade.getClass().getSimpleName(), upgrade.getUpgradableVersionRange()[0], upgrade + .getUpgradableVersionRange()[1], upgrade.getUpgradedVersion()); txn.start(); Connection conn; @@ -408,7 +419,7 @@ private void executeUpgradeCleanup(DbUpgrade upgrade, VersionVO version) { if (scripts != null) { for (InputStream script : scripts) { runScript(conn, script); - LOGGER.debug("Cleanup script " + upgrade.getClass().getSimpleName() + " is executed successfully"); + LOGGER.debug("Cleanup script {} is executed successfully", upgrade.getClass().getSimpleName()); } } txn.commit(); @@ -418,27 +429,27 @@ private void executeUpgradeCleanup(DbUpgrade upgrade, VersionVO version) { version.setUpdated(new Date()); _dao.update(version.getId(), version); txn.commit(); - LOGGER.debug("Upgrade completed for version " + version.getVersion()); + LOGGER.debug("Upgrade completed for version {}", version.getVersion()); } finally { txn.close(); } } protected void executeViewScripts() { - LOGGER.info(String.format("Executing VIEW scripts that are under resource directory [%s].", VIEWS_DIRECTORY)); + LOGGER.info("Executing VIEW scripts that are under resource directory [{}].", VIEWS_DIRECTORY); List filesPathUnderViewsDirectory = FileUtil.getFilesPathsUnderResourceDirectory(VIEWS_DIRECTORY); try (TransactionLegacy txn = TransactionLegacy.open("execute-view-scripts")) { Connection conn = txn.getConnection(); for (String filePath : filesPathUnderViewsDirectory) { - LOGGER.debug(String.format("Executing VIEW script [%s].", filePath)); + LOGGER.debug("Executing VIEW script [{}].", filePath); InputStream viewScript = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath); runScript(conn, viewScript); } - LOGGER.info(String.format("Finished execution of VIEW scripts that are under resource directory [%s].", VIEWS_DIRECTORY)); + LOGGER.info("Finished execution of VIEW scripts that are under resource directory [{}].", VIEWS_DIRECTORY); } catch (SQLException e) { String message = String.format("Unable to execute VIEW scripts due to [%s].", e.getMessage()); LOGGER.error(message, e); @@ -455,43 +466,101 @@ public void check() { throw new CloudRuntimeException("Unable to acquire lock to check for database integrity."); } - try { - initializeDatabaseEncryptors(); - - final CloudStackVersion dbVersion = CloudStackVersion.parse(_dao.getCurrentVersion()); - final String currentVersionValue = this.getClass().getPackage().getImplementationVersion(); + doUpgrades(lock); + } finally { + lock.releaseRef(); + } + } - if (StringUtils.isBlank(currentVersionValue)) { - return; + boolean isStandalone() throws CloudRuntimeException { + return Transaction.execute(new TransactionCallback<>() { + @Override + public Boolean doInTransaction(TransactionStatus status) { + String sql = "SELECT COUNT(*) FROM `cloud`.`mshost` WHERE `state` = 'UP'"; + try (Connection conn = TransactionLegacy.getStandaloneConnection(); + PreparedStatement pstmt = conn.prepareStatement(sql); + ResultSet rs = pstmt.executeQuery()) { + if (rs.next()) { + int count = rs.getInt(1); + return count == 0; + } + } catch (SQLException e) { + String errorMessage = "Unable to check if the management server is running in standalone mode."; + LOGGER.error(errorMessage, e); + return false; + } catch (NullPointerException npe) { + String errorMessage = "Unable to check if the management server is running in standalone mode. Not able to get a Database connection."; + LOGGER.error(errorMessage, npe); + return false; } + return true; + } + }); + } - String csVersion = SystemVmTemplateRegistration.parseMetadataFile(); - final CloudStackVersion sysVmVersion = CloudStackVersion.parse(csVersion); - final CloudStackVersion currentVersion = CloudStackVersion.parse(currentVersionValue); - SystemVmTemplateRegistration.CS_MAJOR_VERSION = String.valueOf(sysVmVersion.getMajorRelease()) + "." + String.valueOf(sysVmVersion.getMinorRelease()); - SystemVmTemplateRegistration.CS_TINY_VERSION = String.valueOf(sysVmVersion.getPatchRelease()); + @VisibleForTesting + protected void doUpgrades(GlobalLock lock) { + try { + initializeDatabaseEncryptors(); - LOGGER.info("DB version = " + dbVersion + " Code Version = " + currentVersion); + final CloudStackVersion dbVersion = CloudStackVersion.parse(_dao.getCurrentVersion()); + final String currentVersionValue = getImplementationVersion(); - if (dbVersion.compareTo(currentVersion) > 0) { - throw new CloudRuntimeException("Database version " + dbVersion + " is higher than management software version " + currentVersionValue); - } + if (StringUtils.isBlank(currentVersionValue)) { + return; + } - if (dbVersion.compareTo(currentVersion) == 0) { - LOGGER.info("DB version and code version matches so no upgrade needed."); - return; - } + String csVersion = parseSystemVmMetadata(); + final CloudStackVersion sysVmVersion = CloudStackVersion.parse(csVersion); + final CloudStackVersion currentVersion = CloudStackVersion.parse(currentVersionValue); + SystemVmTemplateRegistration.CS_MAJOR_VERSION = sysVmVersion.getMajorRelease() + "." + sysVmVersion.getMinorRelease(); + SystemVmTemplateRegistration.CS_TINY_VERSION = String.valueOf(sysVmVersion.getPatchRelease()); + + LOGGER.info("DB version = {} Code Version = {}", dbVersion, currentVersion); + if (dbVersion.compareTo(currentVersion) > 0) { + throw new CloudRuntimeException("Database version " + dbVersion + " is higher than management software version " + currentVersionValue); + } + + if (dbVersion.compareTo(currentVersion) == 0) { + LOGGER.info("DB version and code version matches so no upgrade needed."); + return; + } + + if (isStandalone()) { upgrade(dbVersion, currentVersion); - } finally { - lock.unlock(); + } else { + String errorMessage = "Database upgrade is required but the management server is running in a clustered environment. " + + "Please perform the database upgrade when the management server is not running in a clustered environment."; + LOGGER.error(errorMessage); + handleClusteredUpgradeRequired(); // allow tests to override behavior } } finally { - lock.releaseRef(); + lock.unlock(); } } - private void initializeDatabaseEncryptors() { + /** + * Hook that is called when an upgrade is required but the management server is clustered. + * Default behavior is to exit the JVM, tests can override to throw instead. + */ + @VisibleForTesting + protected void handleClusteredUpgradeRequired() { + System.exit(5); // I would prefer ServerDaemon.abort(errorMessage) but that would create a dependency hell + } + + @VisibleForTesting + protected String getImplementationVersion() { + return this.getClass().getPackage().getImplementationVersion(); + } + + @VisibleForTesting + protected String parseSystemVmMetadata() { + return SystemVmTemplateRegistration.parseMetadataFile(); + } + + // Make this protected so tests can noop it out + protected void initializeDatabaseEncryptors() { TransactionLegacy txn = TransactionLegacy.open("initializeDatabaseEncryptors"); txn.start(); String errorMessage = "Unable to get the database connections"; @@ -520,7 +589,7 @@ private void decryptInit(Connection conn) throws SQLException { ResultSet result = pstmt.executeQuery()) { if (result.next()) { String init = result.getString(1); - LOGGER.info("init = " + DBEncryptionUtil.decrypt(init)); + LOGGER.info("init = {}", DBEncryptionUtil.decrypt(init)); } } } @@ -551,21 +620,11 @@ public String getUpgradedVersion() { return upgradedVersion; } - @Override - public boolean supportsRollingUpgrade() { - return false; - } - @Override public InputStream[] getPrepareScripts() { return new InputStream[0]; } - @Override - public void performDataMigration(Connection conn) { - - } - @Override public InputStream[] getCleanupScripts() { return new InputStream[0]; diff --git a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseVersionHierarchy.java b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseVersionHierarchy.java index 445a59310fb4..377b2f913755 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseVersionHierarchy.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseVersionHierarchy.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.upgrade; +import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -96,7 +97,9 @@ public DbUpgrade[] getPath(final CloudStackVersion fromVersion, final CloudStack // we cannot find the version specified, so get the // most recent one immediately before this version if (!contains(fromVersion)) { - return getPath(getRecentVersion(fromVersion), toVersion); + DbUpgrade[] dbUpgrades = getPath(getRecentVersion(fromVersion), toVersion); + return Arrays.stream(dbUpgrades).filter(up -> CloudStackVersion.compare(up.getUpgradedVersion(), fromVersion.toString()) > 0) + .toArray(DbUpgrade[]::new); } final Predicate predicate; 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 9b1420f22c37..2acb4138d234 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/SystemVmTemplateRegistration.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/SystemVmTemplateRegistration.java @@ -28,7 +28,6 @@ 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; @@ -51,6 +50,7 @@ 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.cloudstack.utils.server.ServerPropertiesUtil; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -62,6 +62,8 @@ import com.cloud.dc.dao.ClusterDaoImpl; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.DataCenterDaoImpl; +import com.cloud.dc.dao.DataCenterDetailsDao; +import com.cloud.dc.dao.DataCenterDetailsDaoImpl; import com.cloud.hypervisor.Hypervisor; import com.cloud.storage.DataStoreRole; import com.cloud.storage.GuestOSVO; @@ -103,14 +105,20 @@ public class SystemVmTemplateRegistration { private static final String METADATA_FILE = TEMPLATES_PATH + METADATA_FILE_NAME; public static final String TEMPORARY_SECONDARY_STORE = "tmp"; private static final String PARTIAL_TEMPLATE_FOLDER = String.format("/template/tmpl/%d/", Account.ACCOUNT_ID_SYSTEM); - private static final String storageScriptsDir = "scripts/storage/secondary"; + protected static final String STORAGE_SCRIPTS_DIR = "scripts/storage/secondary"; private static final Integer OTHER_LINUX_ID = 99; - private static Integer LINUX_12_ID = 363; + protected static Integer LINUX_12_ID = 363; private static final Integer SCRIPT_TIMEOUT = 1800000; private static final Integer LOCK_WAIT_TIMEOUT = 1200; + protected static final String TEMPLATE_DOWNLOAD_URL_KEY = "downloadurl"; + protected static final String TEMPLATES_DOWNLOAD_REPOSITORY_KEY = "downloadrepository"; + protected static final String TEMPLATES_CUSTOM_DOWNLOAD_REPOSITORY_KEY = "system.vm.templates.download.repository"; protected static final List DOWNLOADABLE_TEMPLATE_ARCH_TYPES = Arrays.asList( + CPU.CPUArch.amd64, CPU.CPUArch.arm64 ); + protected static final String MINIMUM_SYSTEM_VM_VERSION_KEY = "minreq.sysvmtemplate.version"; + protected static final String DEFAULT_SYSTEM_VM_GUEST_OS_NAME = "Debian GNU/Linux 12 (64-bit)"; public static String CS_MAJOR_VERSION = null; public static String CS_TINY_VERSION = null; @@ -134,7 +142,9 @@ public class SystemVmTemplateRegistration { @Inject ConfigurationDao configurationDao; @Inject - private GuestOSDao guestOSDao; + DataCenterDetailsDao dataCenterDetailsDao; + @Inject + GuestOSDao guestOSDao; private String systemVmTemplateVersion; @@ -142,6 +152,7 @@ public class SystemVmTemplateRegistration { public SystemVmTemplateRegistration() { dataCenterDao = new DataCenterDaoImpl(); + dataCenterDetailsDao = new DataCenterDetailsDaoImpl(); vmTemplateDao = new VMTemplateDaoImpl(); vmTemplateZoneDao = new VMTemplateZoneDaoImpl(); templateDataStoreDao = new BasicTemplateDataStoreDaoImpl(); @@ -155,33 +166,14 @@ public SystemVmTemplateRegistration() { } /** - * Convenience constructor method to use when there is no system VM template change for a new version. + * Convenience constructor method to use when there is no system VM Template change for a new version. */ public SystemVmTemplateRegistration(String systemVmTemplateVersion) { this(); this.systemVmTemplateVersion = systemVmTemplateVersion; } - public static String getMountCommand(String nfsVersion, String device, String dir) { - String cmd = MOUNT_COMMAND_BASE; - if (StringUtils.isNotBlank(nfsVersion)) { - cmd = String.format("%s -o vers=%s", cmd, nfsVersion); - } - return String.format("%s %s %s", cmd, device, dir); - } - - public String getSystemVmTemplateVersion() { - if (StringUtils.isEmpty(systemVmTemplateVersion)) { - return String.format("%s.%s", CS_MAJOR_VERSION, CS_TINY_VERSION); - } - return systemVmTemplateVersion; - } - - public File getTempDownloadDir() { - return tempDownloadDir; - } - - private static class SystemVMTemplateDetails { + protected static class SystemVMTemplateDetails { Long id; String uuid; String name; @@ -312,19 +304,19 @@ public void setUpdated(Date updated) { } } - public static final List> hypervisorList = Arrays.asList( + protected static final List> AVAILABLE_SYSTEM_TEMPLATES_HYPERVISOR_ARCH_LIST = 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) + new Pair<>(Hypervisor.HypervisorType.VMware, CPU.CPUArch.amd64), + new Pair<>(Hypervisor.HypervisorType.XenServer, CPU.CPUArch.amd64), + new Pair<>(Hypervisor.HypervisorType.Hyperv, CPU.CPUArch.amd64), + new Pair<>(Hypervisor.HypervisorType.LXC, CPU.CPUArch.amd64), + new Pair<>(Hypervisor.HypervisorType.Ovm3, CPU.CPUArch.amd64) ); - public static final Map NewTemplateMap = new HashMap<>(); + protected static final List METADATA_TEMPLATE_LIST = new ArrayList<>(); - public static final Map RouterTemplateConfigurationNames = new HashMap<>() { + protected static final Map ROUTER_TEMPLATE_CONFIGURATION_NAMES = new HashMap<>() { { put(Hypervisor.HypervisorType.KVM, "router.template.kvm"); put(Hypervisor.HypervisorType.VMware, "router.template.vmware"); @@ -335,18 +327,7 @@ public void setUpdated(Date updated) { } }; - public static Map hypervisorGuestOsMap = new HashMap<>() { - { - 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_12_ID); - put(Hypervisor.HypervisorType.LXC, LINUX_12_ID); - put(Hypervisor.HypervisorType.Ovm3, LINUX_12_ID); - } - }; - - public static final Map hypervisorImageFormat = new HashMap() { + protected static final Map HYPERVISOR_IMAGE_FORMAT_MAP = new HashMap<>() { { put(Hypervisor.HypervisorType.KVM, ImageFormat.QCOW2); put(Hypervisor.HypervisorType.XenServer, ImageFormat.VHD); @@ -357,69 +338,27 @@ public void setUpdated(Date updated) { } }; - public boolean validateIfSeeded(TemplateDataStoreVO templDataStoreVO, String url, String path, String nfsVersion) { - String filePath = null; - try { - filePath = Files.createTempDirectory(TEMPORARY_SECONDARY_STORE).toString(); - if (filePath == null) { - throw new CloudRuntimeException("Failed to create temporary directory to mount secondary store"); - } - mountStore(url, filePath, nfsVersion); - int lastIdx = path.lastIndexOf(File.separator); - String partialDirPath = path.substring(0, lastIdx); - String templatePath = filePath + File.separator + partialDirPath; - File templateProps = new File(templatePath + "/template.properties"); - if (templateProps.exists()) { - Pair templateSizes = readTemplatePropertiesSizes(templatePath + "/template.properties"); - updateSeededTemplateDetails(templDataStoreVO.getTemplateId(), templDataStoreVO.getDataStoreId(), - templateSizes.first(), templateSizes.second()); - LOGGER.info("SystemVM template already seeded, skipping registration"); - return true; - } - LOGGER.info("SystemVM template not seeded"); - return false; - } catch (Exception e) { - LOGGER.error("Failed to verify if the template is seeded", e); - throw new CloudRuntimeException("Failed to verify if the template is seeded", e); - } finally { - unmountStore(filePath); - try { - Files.delete(Path.of(filePath)); - } catch (IOException e) { - LOGGER.error(String.format("Failed to delete temporary directory: %s", filePath)); - } + protected static Map hypervisorGuestOsMap = new HashMap<>() { + { + 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_12_ID); + put(Hypervisor.HypervisorType.LXC, LINUX_12_ID); + put(Hypervisor.HypervisorType.Ovm3, LINUX_12_ID); } + }; + + private static boolean isRunningInTest() { + return "true".equalsIgnoreCase(System.getProperty("test.mode")); } 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()); - } + sb.append(", arch: ").append(arch == null ? CPU.CPUArch.amd64.getType() : arch.getType()); return sb.toString(); } - 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()); - } - 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. *

@@ -460,28 +399,124 @@ private static String fetchTemplatesPath() { throw new CloudRuntimeException(errMsg); } - private List getEligibleZoneIds() { - List zoneIds = new ArrayList<>(); - List stores = imageStoreDao.findByProtocol("nfs"); - for (ImageStoreVO store : stores) { - if (!zoneIds.contains(store.getDataCenterId())) { - zoneIds.add(store.getDataCenterId()); + protected static void cleanupStore(Long templateId, String filePath) { + String destTempFolder = filePath + PARTIAL_TEMPLATE_FOLDER + String.valueOf(templateId); + try { + Files.deleteIfExists(Paths.get(destTempFolder)); + } catch (IOException e) { + LOGGER.error("Failed to cleanup mounted store at: {}", filePath, e); + } + } + + protected static Pair readTemplatePropertiesSizes(String path) { + File tmpFile = new File(path); + Long size = null; + Long physicalSize = 0L; + try (FileReader fr = new FileReader(tmpFile); BufferedReader brf = new BufferedReader(fr);) { + String line = null; + while ((line = brf.readLine()) != null) { + if (line.startsWith("size=")) { + physicalSize = Long.parseLong(line.split("=")[1]); + } else if (line.startsWith("virtualsize=")) { + size = Long.parseLong(line.split("=")[1]); + } + if (size == null) { + size = physicalSize; + } } + } catch (IOException ex) { + LOGGER.warn("Failed to read from template.properties", ex); } - return zoneIds; + return new Pair<>(size, physicalSize); } - 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); + protected static MetadataTemplateDetails getMetadataTemplateDetails(Hypervisor.HypervisorType hypervisorType, + CPU.CPUArch arch) { + return METADATA_TEMPLATE_LIST + .stream() + .filter(x -> Objects.equals(x.getHypervisorType(), hypervisorType) && + Objects.equals(x.getArch(), arch)) + .findFirst() + .orElse(null); + } + + protected static String getMetadataFilePath() { + return METADATA_FILE; + } + + protected static Ini.Section getMetadataSectionForHypervisorAndArch(Ini ini, + Hypervisor.HypervisorType hypervisorType, CPU.CPUArch arch) { + String key = String.format("%s-%s", hypervisorType.name().toLowerCase(), + arch.getType().toLowerCase()); + Ini.Section section = ini.get(key); + if (section == null && !Hypervisor.HypervisorType.KVM.equals(hypervisorType)) { + key = String.format("%s", hypervisorType.name().toLowerCase()); + section = ini.get(key); + } + return section; + } + + protected static String getMountCommand(String nfsVersion, String device, String dir) { + String cmd = MOUNT_COMMAND_BASE; + if (StringUtils.isNotBlank(nfsVersion)) { + cmd = String.format("%s -o vers=%s", cmd, nfsVersion); + } + return String.format("%s %s %s", cmd, device, dir); + } + + /** + * This method parses the metadata file consisting of the system VM Templates information + * @return the version of the system VM Template that is to be used. This is done in order + * to fallback on the latest available version of the system VM Template when there doesn't + * exist a template corresponding to the current code version. + */ + public static String parseMetadataFile() { + String metadataFilePath = getMetadataFilePath(); + String errMsg = String.format("Failed to parse system VM 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); } - String url = storeVO.getUrl(); - Long storeId = storeVO.getId(); - return new Pair<>(url, storeId); + Ini.Section defaultSection = ini.get("default"); + String defaultDownloadRepository = defaultSection.get(TEMPLATES_DOWNLOAD_REPOSITORY_KEY); + String customDownloadRepository = ServerPropertiesUtil.getProperty(TEMPLATES_CUSTOM_DOWNLOAD_REPOSITORY_KEY); + boolean updateCustomDownloadRepository = StringUtils.isNotBlank(customDownloadRepository) && + StringUtils.isNotBlank(defaultDownloadRepository); + for (Pair hypervisorTypeArchPair : AVAILABLE_SYSTEM_TEMPLATES_HYPERVISOR_ARCH_LIST) { + String key = String.format("%s-%s", hypervisorTypeArchPair.first().name().toLowerCase(), + hypervisorTypeArchPair.second().getType().toLowerCase()); + Ini.Section section = getMetadataSectionForHypervisorAndArch(ini, hypervisorTypeArchPair.first(), + hypervisorTypeArchPair.second()); + if (section == null) { + LOGGER.error("Failed to find details for {} in template metadata file: {}", + getHypervisorArchLog(hypervisorTypeArchPair.first(), hypervisorTypeArchPair.second()), + metadataFilePath); + continue; + } + String url = section.get(TEMPLATE_DOWNLOAD_URL_KEY); + if (StringUtils.isNotBlank(url) && updateCustomDownloadRepository) { + url = url.replaceFirst(defaultDownloadRepository.trim(), + customDownloadRepository.trim()); + LOGGER.debug("Updated download URL for {} using custom repository to {}", key, url); + } + METADATA_TEMPLATE_LIST.add(new MetadataTemplateDetails( + hypervisorTypeArchPair.first(), + section.get("templatename"), + section.get("filename"), + url, + section.get("checksum"), + hypervisorTypeArchPair.second(), + section.get("guestos"))); + } + return defaultSection.get("version").trim(); } public static void mountStore(String storeUrl, String path, String nfsVersion) { @@ -500,6 +535,64 @@ public static void mountStore(String storeUrl, String path, String nfsVersion) { } } + public static void unmountStore(String filePath) { + try { + LOGGER.info("Unmounting store"); + String umountCmd = String.format(UMOUNT_COMMAND, filePath); + Script.runSimpleBashScript(umountCmd); + try { + Files.deleteIfExists(Paths.get(filePath)); + } catch (IOException e) { + LOGGER.error(String.format("Failed to cleanup mounted store at: %s", filePath), e); + } + } catch (Exception e) { + String msg = String.format("Failed to unmount store mounted at %s", filePath); + LOGGER.error(msg, e); + throw new CloudRuntimeException(msg, e); + } + } + + protected File getTempDownloadDir() { + return tempDownloadDir; + } + + protected void readTemplateProperties(String path, SystemVMTemplateDetails details) { + Pair templateSizes = readTemplatePropertiesSizes(path); + details.setSize(templateSizes.first()); + details.setPhysicalSize(templateSizes.second()); + } + + protected List getEligibleZoneIds() { + List zoneIds = new ArrayList<>(); + List stores = imageStoreDao.findByProtocol("nfs"); + for (ImageStoreVO store : stores) { + if (!zoneIds.contains(store.getDataCenterId())) { + zoneIds.add(store.getDataCenterId()); + } + } + return zoneIds; + } + + 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); + LOGGER.error(errMsg); + throw new CloudRuntimeException(errMsg); + } + String url = storeVO.getUrl(); + Long storeId = storeVO.getId(); + return new Pair<>(url, storeId); + } + + protected String getSystemVmTemplateVersion() { + if (StringUtils.isEmpty(systemVmTemplateVersion)) { + return String.format("%s.%s", CS_MAJOR_VERSION, CS_TINY_VERSION); + } + return systemVmTemplateVersion; + } + private VMTemplateVO createTemplateObjectInDB(SystemVMTemplateDetails details) { Long templateId = vmTemplateDao.getNextInSequence(Long.class, "id"); VMTemplateVO template = new VMTemplateVO(); @@ -513,7 +606,7 @@ private VMTemplateVO createTemplateObjectInDB(SystemVMTemplateDetails details) { template.setBits(64); template.setAccountId(Account.ACCOUNT_ID_SYSTEM); template.setUrl(details.getUrl()); - template.setChecksum(details.getChecksum()); + template.setChecksum(DigestHelper.prependAlgorithm(details.getChecksum())); template.setEnablePassword(false); template.setDisplayText(details.getName()); template.setFormat(details.getFormat()); @@ -527,7 +620,7 @@ private VMTemplateVO createTemplateObjectInDB(SystemVMTemplateDetails details) { return template; } - private VMTemplateZoneVO createOrUpdateTemplateZoneEntry(long zoneId, long templateId) { + protected VMTemplateZoneVO createOrUpdateTemplateZoneEntry(long zoneId, long templateId) { VMTemplateZoneVO templateZoneVO = vmTemplateZoneDao.findByZoneTemplate(zoneId, templateId); if (templateZoneVO == null) { templateZoneVO = new VMTemplateZoneVO(zoneId, templateId, new java.util.Date()); @@ -541,33 +634,37 @@ private VMTemplateZoneVO createOrUpdateTemplateZoneEntry(long zoneId, long templ return templateZoneVO; } - private void createCrossZonesTemplateZoneRefEntries(Long templateId) { + protected void createCrossZonesTemplateZoneRefEntries(Long templateId) { List dcs = dataCenterDao.listAll(); for (DataCenterVO dc : dcs) { VMTemplateZoneVO templateZoneVO = createOrUpdateTemplateZoneEntry(dc.getId(), templateId); if (templateZoneVO == null) { - throw new CloudRuntimeException(String.format("Failed to create template_zone_ref record for the systemVM template (id: %s) and zone: %s", templateId, dc)); + throw new CloudRuntimeException(String.format("Failed to create template-zone record for the system " + + "VM Template (ID : %d) and zone: %s", templateId, dc)); } } } - private void createTemplateStoreRefEntry(SystemVMTemplateDetails details) { - TemplateDataStoreVO templateDataStoreVO = new TemplateDataStoreVO(details.storeId, details.getId(), details.getCreated(), 0, - VMTemplateStorageResourceAssoc.Status.NOT_DOWNLOADED, null, null, null, details.getInstallPath(), details.getUrl()); + protected void createTemplateStoreRefEntry(SystemVMTemplateDetails details) { + TemplateDataStoreVO templateDataStoreVO = new TemplateDataStoreVO(details.getStoreId(), details.getId(), + details.getCreated(), 0, VMTemplateStorageResourceAssoc.Status.NOT_DOWNLOADED, + null, null, null, details.getInstallPath(), details.getUrl()); templateDataStoreVO.setDataStoreRole(DataStoreRole.Image); templateDataStoreVO = templateDataStoreDao.persist(templateDataStoreVO); if (templateDataStoreVO == null) { - throw new CloudRuntimeException(String.format("Failed to create template_store_ref record for the systemVM template for hypervisor: %s", details.getHypervisorType().name())); + throw new CloudRuntimeException(String.format("Failed to create template-store record for the system VM " + + "template (ID : %d) and store (ID: %d)", details.getId(), details.getStoreId())); } } - public void updateTemplateDetails(SystemVMTemplateDetails details) { + protected void updateTemplateDetails(SystemVMTemplateDetails details) { VMTemplateVO template = vmTemplateDao.findById(details.getId()); template.setSize(details.getSize()); template.setState(VirtualMachineTemplate.State.Active); vmTemplateDao.update(template.getId(), template); - TemplateDataStoreVO templateDataStoreVO = templateDataStoreDao.findByStoreTemplate(details.getStoreId(), template.getId()); + TemplateDataStoreVO templateDataStoreVO = templateDataStoreDao.findByStoreTemplate(details.getStoreId(), + template.getId()); templateDataStoreVO.setSize(details.getSize()); templateDataStoreVO.setPhysicalSize(details.getPhysicalSize()); templateDataStoreVO.setDownloadPercent(100); @@ -576,11 +673,11 @@ public void updateTemplateDetails(SystemVMTemplateDetails details) { templateDataStoreVO.setState(ObjectInDataStoreStateMachine.State.Ready); boolean updated = templateDataStoreDao.update(templateDataStoreVO.getId(), templateDataStoreVO); if (!updated) { - throw new CloudRuntimeException("Failed to update template_store_ref entry for registered systemVM template"); + throw new CloudRuntimeException("Failed to update template-store record for registered system VM Template"); } } - public void updateSeededTemplateDetails(long templateId, long storeId, long size, long physicalSize) { + protected void updateSeededTemplateDetails(long templateId, long storeId, long size, long physicalSize) { VMTemplateVO template = vmTemplateDao.findById(templateId); template.setSize(size); vmTemplateDao.update(template.getId(), template); @@ -591,128 +688,113 @@ public void updateSeededTemplateDetails(long templateId, long storeId, long size templateDataStoreVO.setLastUpdated(new Date(DateUtil.currentGMTTime().getTime())); boolean updated = templateDataStoreDao.update(templateDataStoreVO.getId(), templateDataStoreVO); if (!updated) { - throw new CloudRuntimeException("Failed to update template_store_ref entry for seeded systemVM template"); + throw new CloudRuntimeException("Failed to update template-store record for seeded system VM Template"); } } - public void updateSystemVMEntries(Long templateId, Hypervisor.HypervisorType hypervisorType) { + protected void updateSystemVMEntries(Long templateId, Hypervisor.HypervisorType hypervisorType) { vmInstanceDao.updateSystemVmTemplateId(templateId, hypervisorType); } - private void updateSystemVmTemplateGuestOsId() { - String systemVmGuestOsName = "Debian GNU/Linux 12 (64-bit)"; // default + protected void updateHypervisorGuestOsMap() { 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); + GuestOSVO guestOS = guestOSDao.findOneByDisplayName(DEFAULT_SYSTEM_VM_GUEST_OS_NAME); + if (guestOS == null) { + LOGGER.warn("Couldn't find Guest OS by name [{}] to update system VM Template guest OS ID", + DEFAULT_SYSTEM_VM_GUEST_OS_NAME); + return; } + LOGGER.debug("Updating system VM Template guest OS [{}] ID", DEFAULT_SYSTEM_VM_GUEST_OS_NAME); + 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()); + LOGGER.warn("Couldn't update System VM 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()); - if (!updated) { - throw new CloudRuntimeException(String.format("Failed to update configuration parameter %s", config.getKey())); - } + protected void updateConfigurationParams(Hypervisor.HypervisorType hypervisorType, String templateName, Long zoneId) { + String configName = ROUTER_TEMPLATE_CONFIGURATION_NAMES.get(hypervisorType); + boolean updated = configurationDao.update(configName, templateName); + if (!updated) { + throw new CloudRuntimeException(String.format("Failed to update configuration parameter %s", configName)); } - } - - private static Pair readTemplatePropertiesSizes(String path) { - File tmpFile = new File(path); - Long size = null; - Long physicalSize = 0L; - try (FileReader fr = new FileReader(tmpFile); BufferedReader brf = new BufferedReader(fr);) { - String line = null; - while ((line = brf.readLine()) != null) { - if (line.startsWith("size=")) { - physicalSize = Long.parseLong(line.split("=")[1]); - } else if (line.startsWith("virtualsize=")) { - size = Long.parseLong(line.split("=")[1]); - } - if (size == null) { - size = physicalSize; - } - } - } catch (IOException ex) { - LOGGER.warn("Failed to read from template.properties", ex); + if (zoneId != null) { + dataCenterDetailsDao.removeDetail(zoneId, configName); + } + updated = configurationDao.update(MINIMUM_SYSTEM_VM_VERSION_KEY, getSystemVmTemplateVersion()); + if (!updated) { + throw new CloudRuntimeException(String.format("Failed to update configuration parameter %s", configName)); + } + if (zoneId != null) { + dataCenterDetailsDao.removeDetail(zoneId, MINIMUM_SYSTEM_VM_VERSION_KEY); } - return new Pair<>(size, physicalSize); - } - - public static void readTemplateProperties(String path, SystemVMTemplateDetails details) { - Pair templateSizes = readTemplatePropertiesSizes(path); - details.setSize(templateSizes.first()); - details.setPhysicalSize(templateSizes.second()); } - private void updateTemplateTablesOnFailure(long templateId) { + protected void updateTemplateEntriesOnFailure(long templateId) { VMTemplateVO template = vmTemplateDao.createForUpdate(templateId); template.setState(VirtualMachineTemplate.State.Inactive); vmTemplateDao.update(template.getId(), template); vmTemplateDao.remove(templateId); - TemplateDataStoreVO templateDataStoreVO = templateDataStoreDao.findByTemplate(template.getId(), DataStoreRole.Image); - templateDataStoreDao.remove(templateDataStoreVO.getId()); - } - - public static void unmountStore(String filePath) { - try { - LOGGER.info("Unmounting store"); - String umountCmd = String.format(UMOUNT_COMMAND, filePath); - Script.runSimpleBashScript(umountCmd); - try { - Files.deleteIfExists(Paths.get(filePath)); - } catch (IOException e) { - LOGGER.error(String.format("Failed to cleanup mounted store at: %s", filePath), e); - } - } catch (Exception e) { - String msg = String.format("Failed to unmount store mounted at %s", filePath); - LOGGER.error(msg, e); - throw new CloudRuntimeException(msg, e); + TemplateDataStoreVO templateDataStoreVO = templateDataStoreDao.findByTemplate(template.getId(), + DataStoreRole.Image); + if (templateDataStoreVO == null) { + return; } + templateDataStoreDao.remove(templateDataStoreVO.getId()); } - private void setupTemplate(String templateName, Hypervisor.HypervisorType hypervisor, CPU.CPUArch arch, - String destTempFolder) throws CloudRuntimeException { - String setupTmpltScript = Script.findScript(storageScriptsDir, "setup-sysvm-tmplt"); + protected void setupTemplateOnStore(String templateName, MetadataTemplateDetails templateDetails, + String destTempFolder) throws CloudRuntimeException { + String setupTmpltScript = Script.findScript(STORAGE_SCRIPTS_DIR, "setup-sysvm-tmplt"); if (setupTmpltScript == null) { - throw new CloudRuntimeException("Unable to find the createtmplt.sh"); + throw new CloudRuntimeException("Unable to find the setup-sysvm-tmplt script"); } Script scr = new Script(setupTmpltScript, SCRIPT_TIMEOUT, LOGGER); scr.add("-u", templateName); - 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("-h", templateDetails.getHypervisorType().name().toLowerCase(Locale.ROOT)); scr.add("-d", destTempFolder); String result = scr.execute(); if (result != null) { - String errMsg = String.format("failed to create template: %s ", result); + String errMsg = String.format("Failed to create Template: %s ", result); LOGGER.error(errMsg); throw new CloudRuntimeException(errMsg); } } - 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) { + /** + * Register or update a system VM Template record and seed it on the target store. + * + * @param name display name of the template + * @param templateDetails metadata for the template + * @param url download URL of the template + * @param checksum expected checksum of the template file + * @param format image format of the template + * @param guestOsId guest OS id + * @param storeId target image store id + * @param templateId existing template id if present, otherwise {@code null} + * @param filePath temporary mount path for the store + * @param templateDataStoreVO existing template-store mapping; may be {@code null} + * @return the id of the template that was created or updated + */ + protected Long performTemplateRegistrationOperations(String name, MetadataTemplateDetails templateDetails, + 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, name, created, - url, checksum, format, (int) guestOsId, hypervisor, arch, storeId); + SystemVMTemplateDetails details = new SystemVMTemplateDetails(templateName, name, created, url, checksum, + format, (int) guestOsId, templateDetails.getHypervisorType(), templateDetails.getArch(), storeId); if (templateId == null) { VMTemplateVO template = createTemplateObjectInDB(details); if (template == null) { - throw new CloudRuntimeException(String.format("Failed to register template for hypervisor: %s", hypervisor.name())); + throw new CloudRuntimeException(String.format("Failed to register Template for hypervisor: %s", + templateDetails.getHypervisorType().name())); } templateId = template.getId(); } @@ -721,153 +803,126 @@ private Long performTemplateRegistrationOperations(Hypervisor.HypervisorType hyp details.setId(templateId); String destTempFolderName = String.valueOf(templateId); String destTempFolder = filePath + PARTIAL_TEMPLATE_FOLDER + destTempFolderName; - details.setInstallPath(PARTIAL_TEMPLATE_FOLDER + destTempFolderName + File.separator + templateName + "." + hypervisorImageFormat.get(hypervisor).getFileExtension()); + details.setInstallPath(String.format("%s%s%s%s.%s", PARTIAL_TEMPLATE_FOLDER, destTempFolderName, + File.separator, templateName, + HYPERVISOR_IMAGE_FORMAT_MAP.get(templateDetails.getHypervisorType()).getFileExtension())); if (templateDataStoreVO == null) { createTemplateStoreRefEntry(details); } - setupTemplate(templateName, hypervisor, arch, destTempFolder); + setupTemplateOnStore(templateName, templateDetails, destTempFolder); readTemplateProperties(destTempFolder + "/template.properties", details); details.setUpdated(new Date(DateUtil.currentGMTTime().getTime())); updateTemplateDetails(details); return templateId; } - public void registerTemplate(Hypervisor.HypervisorType hypervisor, String name, Long storeId, - VMTemplateVO templateVO, TemplateDataStoreVO templateDataStoreVO, String filePath) { + /** + * Add an existing system VM Template to a secondary image store and update related DB entries. + * + * @param templateVO the existing VM template (must not be null) + * @param templateDetails the metadata details of the template to be added + * @param templateDataStoreVO optional existing template-store mapping; may be null + * @param zoneId zone id where the operation is performed + * @param storeId target image store id + * @param filePath temporary mount path for the store + * @throws CloudRuntimeException on failure; the method attempts rollback/cleanup + */ + protected void addExistingTemplateToStore(VMTemplateVO templateVO, MetadataTemplateDetails templateDetails, + TemplateDataStoreVO templateDataStoreVO, long zoneId, Long storeId, String filePath) { try { - performTemplateRegistrationOperations(hypervisor, name, templateVO.getArch(), templateVO.getUrl(), + performTemplateRegistrationOperations(templateVO.getName(), templateDetails, 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); + String errMsg = String.format("Failed to add %s to store ID: %d, zone ID: %d", templateVO, storeId, zoneId); 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) { + /** + * Registers a new system VM Template for the given hypervisor/arch when no existing template is present. + * + * @param name the name of the new template + * @param templateDetails the metadata details of the template to be registered + * @param zoneId the zone id for which the new template should be seeded + * @param storeId the store id on which the new template will be seeded + * @param filePath temporary mount path for the store + * @throws CloudRuntimeException on failure; the method attempts rollback/cleanup + */ + protected void registerNewTemplate(String name, MetadataTemplateDetails templateDetails, long zoneId, Long storeId, + String filePath) { Long templateId = null; + Hypervisor.HypervisorType hypervisor = templateDetails.getHypervisorType(); try { - 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); + templateId = performTemplateRegistrationOperations(name, templateDetails, templateDetails.getUrl(), + templateDetails.getChecksum(), HYPERVISOR_IMAGE_FORMAT_MAP.get(hypervisor), + hypervisorGuestOsMap.get(hypervisor), storeId, null, filePath, null); + updateConfigurationParams(hypervisor, name, zoneId); updateSystemVMEntries(templateId, hypervisor); } catch (Exception e) { - String errMsg = String.format("Failed to register template for hypervisor: %s", hypervisor); + String errMsg = String.format("Failed to register Template for hypervisor: %s", hypervisor); LOGGER.error(errMsg, e); if (templateId != null) { - updateTemplateTablesOnFailure(templateId); + updateTemplateEntriesOnFailure(templateId); cleanupStore(templateId, filePath); } throw new CloudRuntimeException(errMsg, e); } } - protected void validateTemplateFileForHypervisorAndArch(Hypervisor.HypervisorType hypervisor, CPU.CPUArch arch) { + /** + * Validate presence and integrity of metadata and local template file for the given hypervisor/arch. + * + * @param hypervisor target hypervisor type + * @param arch target CPU architecture + * @return validated MetadataTemplateDetails + * @throws CloudRuntimeException if template is not available, missing, or checksum validation fails + */ + protected MetadataTemplateDetails getValidatedTemplateDetailsForHypervisorAndArch( + Hypervisor.HypervisorType hypervisor, CPU.CPUArch arch) { + if (!AVAILABLE_SYSTEM_TEMPLATES_HYPERVISOR_ARCH_LIST.contains(new Pair<>(hypervisor, arch))) { + throw new CloudRuntimeException("No system VM Template available for the given hypervisor and arch"); + } MetadataTemplateDetails templateDetails = getMetadataTemplateDetails(hypervisor, arch); + if (templateDetails == null) { + throw new CloudRuntimeException("No template details found for the given hypervisor and arch"); + } File templateFile = getTemplateFile(templateDetails); if (templateFile == null) { throw new CloudRuntimeException("Failed to find local template file"); } - if (isTemplateFileChecksumDifferent(templateDetails, templateFile)) { + if (templateDetails.isFileChecksumDifferent(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; + return templateDetails; } /** - * This method parses the metadata file consisting of the systemVM templates information - * @return the version of the systemvm template that is to be used. This is done in order - * to fallback on the latest available version of the systemVM template when there doesn't - * exist a template corresponding to the current code version. + * Return the local template file. Downloads it if not present locally and url is present. + * + * @param templateDetails template metadata; may set `downloadedFilePath` + * @return the template {@code File} on disk, or {@code null} if not found/downloaded */ - public static String parseMetadataFile() { - 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 { - Files.deleteIfExists(Paths.get(destTempFolder)); - } catch (IOException e) { - LOGGER.error(String.format("Failed to cleanup mounted store at: %s", filePath), e); - } - } - 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())) { + if (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()); + templateFile = new File(getTempDownloadDir(), templateDetails.getFilename()); } if (!templateFile.exists() && !HttpUtils.downloadFileWithProgress(templateDetails.getUrl(), templateFile.getAbsolutePath(), LOGGER)) { + LOGGER.error("Failed to download template for {} using url: {}", + templateDetails.getHypervisorArchLog(), templateDetails.getUrl()); return null; } templateDetails.setDownloadedFilePath(templateFile.getAbsolutePath()); @@ -875,47 +930,53 @@ protected File getTemplateFile(MetadataTemplateDetails templateDetails) { 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) { + /** + * Validate that templates for the provided hypervisor/architecture pairs which are in use and are valid. + * + * If a template is missing or validation fails for any required pair, a + * {@link CloudRuntimeException} is thrown to abort the upgrade. If system VM Template for a hypervisor/arch is + * not considered available then validation is skipped for that pair. + * + * @param hypervisorArchList list of hypervisor/architecture pairs to validate + */ + protected void validateTemplates(List> hypervisorArchList) { boolean templatesFound = true; - for (Pair hypervisorArch : hypervisorsArchInUse) { - MetadataTemplateDetails matchedTemplate = getMetadataTemplateDetails(hypervisorArch.first(), - hypervisorArch.second()); - if (matchedTemplate == null) { - templatesFound = false; - break; - } - File tempFile = getTemplateFile(matchedTemplate); - if (tempFile == null) { - LOGGER.warn("Failed to download template for {}, moving ahead", - matchedTemplate.getHypervisorArchLog()); + for (Pair hypervisorArch : hypervisorArchList) { + if (!AVAILABLE_SYSTEM_TEMPLATES_HYPERVISOR_ARCH_LIST.contains(hypervisorArch)) { + LOGGER.info("No system VM Template available for {}. Skipping validation.", + getHypervisorArchLog(hypervisorArch.first(), hypervisorArch.second())); continue; } - if (isTemplateFileChecksumDifferent(matchedTemplate, tempFile)) { + try { + getValidatedTemplateDetailsForHypervisorAndArch(hypervisorArch.first(), hypervisorArch.second()); + } catch (CloudRuntimeException e) { + LOGGER.error("Validation failed for {}: {}", + getHypervisorArchLog(hypervisorArch.first(), hypervisorArch.second()), e.getMessage()); templatesFound = false; break; } } if (!templatesFound) { - String errMsg = "SystemVm template not found. Cannot upgrade system Vms"; + String errMsg = "SystemVM Template not found. Cannot upgrade system VMs"; LOGGER.error(errMsg); throw new CloudRuntimeException(errMsg); } } - protected void registerTemplatesForZone(long zoneId, String filePath) { + /** + * Register or ensure system VM Templates are present on the NFS store for a given zone. + * + * Mounts the zone image store, enumerates hypervisors and architectures in the zone, + * and for each template either adds an existing template to the store or registers + * a new template as required. + * + * @param zoneId the zone id + * @param storeMountPath temporary mount path for the store + */ + protected void registerTemplatesForZone(long zoneId, String storeMountPath) { Pair storeUrlAndId = getNfsStoreInZone(zoneId); String nfsVersion = getNfsVersion(storeUrlAndId.second()); - mountStore(storeUrlAndId.first(), filePath, nfsVersion); + mountStore(storeUrlAndId.first(), storeMountPath, nfsVersion); List> hypervisorArchList = clusterDao.listDistinctHypervisorsAndArchExcludingExternalType(zoneId); for (Pair hypervisorArch : hypervisorArchList) { @@ -925,7 +986,8 @@ protected void registerTemplatesForZone(long zoneId, String filePath) { if (templateDetails == null) { continue; } - VMTemplateVO templateVO = getRegisteredTemplate(templateDetails.getName(), templateDetails.getArch()); + VMTemplateVO templateVO = getRegisteredTemplate(templateDetails.getName(), + templateDetails.getHypervisorType(), templateDetails.getArch(), templateDetails.getUrl()); if (templateVO != null) { TemplateDataStoreVO templateDataStoreVO = templateDataStoreDao.findByStoreTemplate(storeUrlAndId.second(), templateVO.getId()); @@ -935,22 +997,22 @@ protected void registerTemplatesForZone(long zoneId, String filePath) { continue; } } - registerTemplate(hypervisorType, templateDetails.getName(), storeUrlAndId.second(), templateVO, - templateDataStoreVO, filePath); - updateRegisteredTemplateDetails(templateVO.getId(), templateDetails); + addExistingTemplateToStore(templateVO, templateDetails, templateDataStoreVO, zoneId, + storeUrlAndId.second(), storeMountPath); + updateRegisteredTemplateDetails(templateVO.getId(), templateDetails, zoneId); continue; } - registerTemplateForNonExistingEntries(hypervisorType, templateDetails.getArch(), templateDetails.getName(), - storeUrlAndId, filePath); + registerNewTemplate(templateDetails.getName(), templateDetails, zoneId, storeUrlAndId.second(), + storeMountPath); } } - public void registerTemplates(List> hypervisorsArchInUse) { + protected void registerTemplates(List> hypervisorsArchInUse) { GlobalLock lock = GlobalLock.getInternLock("UpgradeDatabase-Lock"); try { - LOGGER.info("Grabbing lock to register templates."); + LOGGER.info("Grabbing lock to register Templates."); if (!lock.lock(LOCK_WAIT_TIMEOUT)) { - throw new CloudRuntimeException("Unable to acquire lock to register SystemVM template."); + throw new CloudRuntimeException("Unable to acquire lock to register system VM Template."); } try { validateTemplates(hypervisorsArchInUse); @@ -970,13 +1032,13 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { unmountStore(filePath); } catch (Exception e) { unmountStore(filePath); - throw new CloudRuntimeException("Failed to register systemVM template. Upgrade Failed"); + throw new CloudRuntimeException("Failed to register system VM Template. Upgrade Failed"); } } } }); } catch (Exception e) { - throw new CloudRuntimeException("Failed to register systemVM template. Upgrade Failed"); + throw new CloudRuntimeException("Failed to register system VM Template. Upgrade Failed"); } } finally { lock.unlock(); @@ -984,7 +1046,18 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { } } - private void updateRegisteredTemplateDetails(Long templateId, MetadataTemplateDetails templateDetails) { + /** + * Update the DB record for an existing template to mark it as a system template, + * set the guest OS (if resolvable), and propagate the change to system VM entries + * and related configuration for the template's hypervisor. + * + * @param templateId id of the template to update + * @param templateDetails metadata used to update the template record + * @param zoneId zone id whose per-zone details (if any) should be cleared; may be null + * @throws CloudRuntimeException if updating the template record fails + */ + protected void updateRegisteredTemplateDetails(Long templateId, MetadataTemplateDetails templateDetails, + Long zoneId) { VMTemplateVO templateVO = vmTemplateDao.findById(templateId); templateVO.setTemplateType(Storage.TemplateType.SYSTEM); GuestOSVO guestOS = guestOSDao.findOneByDisplayName(templateDetails.getGuestOs()); @@ -993,73 +1066,213 @@ private void updateRegisteredTemplateDetails(Long templateId, MetadataTemplateDe } 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); + String errMsg = String.format("Exception while updating template with id %s to be marked as 'system'", + templateId); LOGGER.error(errMsg); throw new CloudRuntimeException(errMsg); } 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(hypervisorType), templateDetails.getName()); - configParams.put("minreq.sysvmtemplate.version", getSystemVmTemplateVersion()); - updateConfigurationParams(configParams); + updateConfigurationParams(hypervisorType, templateVO.getName(), zoneId); } - private void updateTemplateUrlChecksumAndGuestOsId(VMTemplateVO templateVO, MetadataTemplateDetails templateDetails) { + protected void updateTemplateUrlChecksumAndGuestOsId(VMTemplateVO templateVO, + MetadataTemplateDetails templateDetails) { templateVO.setUrl(templateDetails.getUrl()); - templateVO.setChecksum(templateDetails.getChecksum()); + templateVO.setChecksum(DigestHelper.prependAlgorithm(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", templateDetails.getHypervisorType()); + String errMsg = String.format("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 + /** + * Updates or registers the system VM Template for the given hypervisor/arch if not already present. + * Returns true if a new template was registered. + * If there is an existing system VM Template for the given hypervisor/arch, its details are updated. + * If no existing template is found, new templates are registered for the valid hypervisor/arch which are in use. + */ + protected boolean updateOrRegisterSystemVmTemplate(MetadataTemplateDetails templateDetails, + List> hypervisorArchInUse) { + String systemVmTemplateLog = String.format("%s system VM Template for %s", getSystemVmTemplateVersion(), + templateDetails.getHypervisorArchLog()); + LOGGER.debug("Registering or updating {}", systemVmTemplateLog, + templateDetails.getHypervisorArchLog()); + VMTemplateVO registeredTemplate = getRegisteredTemplate(templateDetails.getName(), + templateDetails.getHypervisorType(), templateDetails.getArch(), templateDetails.getUrl()); 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); - } + LOGGER.info("{} is already registered, updating details for: {}", + systemVmTemplateLog, templateDetails.getHypervisorArchLog(), registeredTemplate); + updateRegisteredTemplateDetails(registeredTemplate.getId(), templateDetails, null); + return false; + } + boolean isHypervisorArchMatchMetadata = hypervisorArchInUse.stream() + .anyMatch(p -> p.first().equals(templateDetails.getHypervisorType()) + && Objects.equals(p.second(), templateDetails.getArch())); + if (!isHypervisorArchMatchMetadata) { + LOGGER.warn("Skipping upgrading {} 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; + } + try { + registerTemplates(hypervisorArchInUse); + 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(hypervisorArchInUse.stream() + .map(x -> String.format("%s-%s", x.first().name(), x.second().name())) + .collect(Collectors.toList()), ",")), e); + } + } + + /** + * Return NFS version for the store: store-specific config if present + * or global config if absent. Returns null if not set. + */ + protected String getNfsVersion(long storeId) { + final String configKey = "secstorage.nfs.version"; + final Map storeDetails = imageStoreDetailsDao.getDetails(storeId); + if (storeDetails != null && storeDetails.containsKey(configKey)) { + return storeDetails.get(configKey); + } + ConfigurationVO globalNfsVersion = configurationDao.findByName(configKey); + if (globalNfsVersion != null) { + return globalNfsVersion.getValue(); + } + return null; + } + + /** + * Validate metadata for the given template's hypervisor/arch and add the existing template + * to the specified secondary store. On success, database entries are created/updated. + * + * @param templateVO template to add + * @param templateDataStoreVO existing template-store mapping; may be null + * @param zoneId zone id where the operation is performed + * @param storeId target image store id + * @param filePath temporary mount path for the store + * @throws CloudRuntimeException on failure; the method attempts rollback/cleanup + */ + public void validateAndAddTemplateToStore(VMTemplateVO templateVO, TemplateDataStoreVO templateDataStoreVO, + long zoneId, long storeId, String filePath) { + MetadataTemplateDetails templateDetails = getValidatedTemplateDetailsForHypervisorAndArch( + templateVO.getHypervisorType(), templateVO.getArch()); + addExistingTemplateToStore(templateVO, templateDetails, templateDataStoreVO, zoneId, storeId, filePath); + } + + /** + * Validate metadata for the given hypervisor/arch and register a new system VM Template + * on the specified store and zone. Creates DB entries and seeds the template on the store. + * + * @param hypervisor hypervisor type + * @param arch cpu architecture + * @param name template name to register + * @param zoneId zone id where the operation is performed + * @param storeId target image store id + * @param filePath temporary mount path for the store + * @throws CloudRuntimeException on failure; the method attempts rollback/cleanup + */ + public void validateAndRegisterNewTemplate(Hypervisor.HypervisorType hypervisor, CPU.CPUArch arch, String name, + long zoneId, long storeId, String filePath) { + MetadataTemplateDetails templateDetails = getValidatedTemplateDetailsForHypervisorAndArch(hypervisor, arch); + registerNewTemplate(name, templateDetails, zoneId, storeId, filePath); + } + + /** + * Check whether the template at the given `path` on NFS `url` is already seeded. + * If found, updates DB with sizes and returns true; otherwise returns false. + * + * @throws CloudRuntimeException on any error + */ + public boolean validateIfSeeded(TemplateDataStoreVO templDataStoreVO, String url, String path, String nfsVersion) { + String filePath = null; + try { + filePath = Files.createTempDirectory(TEMPORARY_SECONDARY_STORE).toString(); + if (filePath == null) { + throw new CloudRuntimeException("Failed to create temporary directory to mount secondary store"); + } + mountStore(url, filePath, nfsVersion); + int lastIdx = path.lastIndexOf(File.separator); + String partialDirPath = path.substring(0, lastIdx); + String templatePath = filePath + File.separator + partialDirPath; + File templateProps = new File(templatePath + "/template.properties"); + if (templateProps.exists()) { + Pair templateSizes = readTemplatePropertiesSizes(templatePath + "/template.properties"); + updateSeededTemplateDetails(templDataStoreVO.getTemplateId(), templDataStoreVO.getDataStoreId(), + templateSizes.first(), templateSizes.second()); + LOGGER.info("System VM template already seeded, skipping registration"); + return true; + } + LOGGER.info("System VM template not seeded"); + return false; + } catch (Exception e) { + LOGGER.error("Failed to verify if the template is seeded", e); + throw new CloudRuntimeException("Failed to verify if the template is seeded", e); + } finally { + unmountStore(filePath); + try { + Files.delete(Path.of(filePath)); + } catch (IOException e) { + LOGGER.error("Failed to delete temporary directory: {}", filePath); } } - return false; } + /** + * Finds a registered system VM Template matching the provided criteria. + * + *

The method first attempts to locate the latest template by {@code templateName}, + * {@code hypervisorType} and {@code arch}. If none is found and a non-blank {@code url} + * is provided, it falls back to searching for an active system template by the + * URL path segment (the substring after the last '/' in the URL).

+ * + * @param templateName the template name to search for + * @param hypervisorType the hypervisor type + * @param arch the CPU architecture + * @param url optional download URL used as a fallback; may be {@code null} or blank + * @return the matching {@code VMTemplateVO} if found; {@code null} otherwise + */ + public VMTemplateVO getRegisteredTemplate(String templateName, Hypervisor.HypervisorType hypervisorType, + CPU.CPUArch arch, String url) { + VMTemplateVO registeredTemplate = vmTemplateDao.findLatestTemplateByName(templateName, hypervisorType, arch); + if (registeredTemplate == null && StringUtils.isNotBlank(url)) { + String urlPath = url.substring(url.lastIndexOf("/") + 1); + LOGGER.debug("No template found by name, falling back to search existing SYSTEM template by " + + "urlPath: {}, hypervisor: {}, arch:{}", urlPath, hypervisorType, arch); + registeredTemplate = vmTemplateDao.findActiveSystemTemplateByHypervisorArchAndUrlPath(hypervisorType, arch, + urlPath); + } + LOGGER.debug("Found existing registered template for hypervisor: {}, arch: {}: {}", hypervisorType, + arch, registeredTemplate); + return registeredTemplate; + } + + /** + * Update or register system VM Templates based on metadata. + * + * Runs the registration logic inside a database transaction: obtains the + * set of hypervisors/architectures in use, iterates over metadata entries + * and attempts to register or update each template. + * + * @param conn retained for compatibility with callers (not used directly) + */ public void updateSystemVmTemplates(final Connection conn) { - LOGGER.debug("Updating System Vm template IDs"); - updateSystemVmTemplateGuestOsId(); + LOGGER.debug("Updating System VM templates"); + updateHypervisorGuestOsMap(); Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(final TransactionStatus status) { @@ -1069,36 +1282,22 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { } catch (final Exception e) { throw new CloudRuntimeException("Exception while getting hypervisor types from clusters", e); } - Collection templateEntries = NewTemplateMap.values(); - for (MetadataTemplateDetails templateDetails : templateEntries) { + for (MetadataTemplateDetails templateDetails : METADATA_TEMPLATE_LIST) { try { - if (registerOrUpdateSystemVmTemplate(templateDetails, hypervisorsInUse)) { + if (updateOrRegisterSystemVmTemplate(templateDetails, hypervisorsInUse)) { break; } } catch (final Exception e) { - String errMsg = "Exception while registering/updating system VM templates for hypervisors in metadata"; + String errMsg = "Exception while registering/updating system VM Templates for hypervisors in metadata"; LOGGER.error(errMsg, e); throw new CloudRuntimeException(errMsg, e); } } - LOGGER.debug("Updating System Vm Template IDs Complete"); + LOGGER.debug("Updating System VM Templates Complete"); } }); } - public String getNfsVersion(long storeId) { - final String configKey = "secstorage.nfs.version"; - final Map storeDetails = imageStoreDetailsDao.getDetails(storeId); - if (storeDetails != null && storeDetails.containsKey(configKey)) { - return storeDetails.get(configKey); - } - ConfigurationVO globalNfsVersion = configurationDao.findByName(configKey); - if (globalNfsVersion != null) { - return globalNfsVersion.getValue(); - } - return null; - } - protected static class MetadataTemplateDetails { private final Hypervisor.HypervisorType hypervisorType; private final String name; @@ -1160,6 +1359,16 @@ public String getDefaultFilePath() { return TEMPLATES_PATH + filename; } + public boolean isFileChecksumDifferent(File file) { + String fileChecksum = DigestHelper.calculateChecksum(file); + if (!fileChecksum.equals(getChecksum())) { + LOGGER.error("Checksum {} for file {} does not match checksum {} from metadata", + fileChecksum, file, getChecksum()); + return true; + } + return false; + } + public String getHypervisorArchLog() { return SystemVmTemplateRegistration.getHypervisorArchLog(hypervisorType, arch); } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade218to22.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade218to22.java index 171357578ee9..a73c458c948a 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade218to22.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade218to22.java @@ -140,7 +140,7 @@ protected void upgradeInstanceGroups(Connection conn) { } } catch (SQLException e) { - throw new CloudRuntimeException("Can't update instance groups ", e); + throw new CloudRuntimeException("Can't update Instance groups ", e); } } @@ -464,7 +464,7 @@ protected void upgradeUserVms(Connection conn, long domainRouterId, long network vm[4] = rs.getString(5); // vm state vms.add(vm); } - logger.debug("Upgrading " + vms.size() + " vms for router " + domainRouterId); + logger.debug("Upgrading " + vms.size() + " Instances for router " + domainRouterId); for (Object[] vm : vms) { String state = (String)vm[4]; @@ -1218,7 +1218,7 @@ private void updateUserStats(Connection conn) { } } } else { - logger.debug("Account id=" + accountId + " doesn't own any user vms and domRs, so skipping user_statistics update"); + logger.debug("Account id=" + accountId + " doesn't own any user Instances and domRs, so skipping user_statistics update"); continue; } } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade2214to30.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade2214to30.java index 524b6a34893b..d4cdbcb9707d 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade2214to30.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade2214to30.java @@ -77,8 +77,6 @@ public void performDataMigration(Connection conn) { encryptData(conn); // drop keys dropKeysIfExist(conn); - //update template ID for system Vms - //updateSystemVms(conn); This is not required as system template update is handled during 4.2 upgrade // update domain network ref updateDomainNetworkRef(conn); // update networks that use redundant routers to the new network offering diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade222to224.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade222to224.java index b891b02ea572..5cb7d4287956 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade222to224.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade222to224.java @@ -153,7 +153,7 @@ private void updateGuestOsType(Connection conn) { } } catch (SQLException e) { - throw new CloudRuntimeException("Unable to update the guest os type for default template as a part of 222 to 224 upgrade", e); + throw new CloudRuntimeException("Unable to update the guest os type for default Template as a part of 222 to 224 upgrade", e); } } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade302to40.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade302to40.java index aa427252585f..bd8ddaa7c498 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade302to40.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade302to40.java @@ -62,7 +62,6 @@ public InputStream[] getPrepareScripts() { @Override public void performDataMigration(Connection conn) { - //updateVmWareSystemVms(conn); This is not required as system template update is handled during 4.2 upgrade correctVRProviders(conn); correctMultiplePhysicaNetworkSetups(conn); addHostDetailsUniqueKey(conn); diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade304to305.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade304to305.java index bb4c73f67b68..38dc90b460dd 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade304to305.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade304to305.java @@ -65,7 +65,6 @@ public void performDataMigration(Connection conn) { addVpcProvider(conn); updateRouterNetworkRef(conn); fixZoneUsingExternalDevices(conn); -// updateSystemVms(conn); fixForeignKeys(conn); encryptClusterDetails(conn); } @@ -81,57 +80,9 @@ public InputStream[] getCleanupScripts() { return new InputStream[] {script}; } - private void updateSystemVms(Connection conn) { - PreparedStatement pstmt = null; - ResultSet rs = null; - boolean VMware = false; - try { - pstmt = conn.prepareStatement("select distinct(hypervisor_type) from `cloud`.`cluster` where removed is null"); - rs = pstmt.executeQuery(); - while (rs.next()) { - if ("VMware".equals(rs.getString(1))) { - VMware = true; - } - } - } catch (SQLException e) { - throw new CloudRuntimeException("Error while iterating through list of hypervisors in use", e); - } - // Just update the VMware system template. Other hypervisor templates are unchanged from previous 3.0.x versions. - logger.debug("Updating VMware System Vms"); - try { - //Get 3.0.5 VMware system Vm template Id - pstmt = conn.prepareStatement("select id from `cloud`.`vm_template` where name = 'systemvm-vmware-3.0.5' and removed is null"); - rs = pstmt.executeQuery(); - if (rs.next()) { - long templateId = rs.getLong(1); - rs.close(); - pstmt.close(); - // change template type to SYSTEM - pstmt = conn.prepareStatement("update `cloud`.`vm_template` set type='SYSTEM' where id = ?"); - pstmt.setLong(1, templateId); - pstmt.executeUpdate(); - pstmt.close(); - // update template ID of system Vms - pstmt = conn.prepareStatement("update `cloud`.`vm_instance` set vm_template_id = ? where type <> 'User' and hypervisor_type = 'VMware'"); - pstmt.setLong(1, templateId); - pstmt.executeUpdate(); - pstmt.close(); - } else { - if (VMware) { - throw new CloudRuntimeException("3.0.5 VMware SystemVm template not found. Cannot upgrade system Vms"); - } else { - logger.warn("3.0.5 VMware SystemVm template not found. VMware hypervisor is not used, so not failing upgrade"); - } - } - } catch (SQLException e) { - throw new CloudRuntimeException("Error while updating VMware systemVm template", e); - } - logger.debug("Updating System Vm Template IDs Complete"); - } - private void addVpcProvider(Connection conn) { //Encrypt config params and change category to Hidden - logger.debug("Adding vpc provider to all physical networks in the system"); + logger.debug("Adding VPC provider to all physical Networks in the system"); PreparedStatement pstmt = null; ResultSet rs = null; try { diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade410to420.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade410to420.java index b78aed3119a4..5c47087b9689 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade410to420.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade410to420.java @@ -665,25 +665,25 @@ private void upgradeVmwareLabels(Connection conn) { newLabel = getNewLabel(rsLabel, trafficTypeVswitchParamValue); try(PreparedStatement update_pstmt = conn.prepareStatement("update physical_network_traffic_types set vmware_network_label = ? where traffic_type = ? and vmware_network_label is not NULL;");) { - logger.debug("Updating vmware label for " + trafficType + " traffic. Update SQL statement is " + pstmt); + logger.debug("Updating VMware label for " + trafficType + " traffic. Update SQL statement is " + pstmt); pstmt.setString(1, newLabel); pstmt.setString(2, trafficType); update_pstmt.executeUpdate(); }catch (SQLException e) { - throw new CloudRuntimeException("Unable to set vmware traffic labels ", e); + throw new CloudRuntimeException("Unable to set VMware traffic labels ", e); } }catch (SQLException e) { - throw new CloudRuntimeException("Unable to set vmware traffic labels ", e); + throw new CloudRuntimeException("Unable to set VMware traffic labels ", e); } }catch (SQLException e) { - throw new CloudRuntimeException("Unable to set vmware traffic labels ", e); + throw new CloudRuntimeException("Unable to set VMware traffic labels ", e); } } }catch (SQLException e) { - throw new CloudRuntimeException("Unable to set vmware traffic labels ", e); + throw new CloudRuntimeException("Unable to set VMware traffic labels ", e); } } catch (SQLException e) { - throw new CloudRuntimeException("Unable to set vmware traffic labels ", e); + throw new CloudRuntimeException("Unable to set VMware traffic labels ", e); } } @@ -1195,9 +1195,9 @@ private void updateGlobalDeploymentPlanner(Connection conn) { plannerName = "FirstFitPlanner"; } else if (globalValue.equals(DeploymentPlanner.AllocationAlgorithm.firstfit.toString())) { plannerName = "FirstFitPlanner"; - } else if (globalValue.equals(DeploymentPlanner.AllocationAlgorithm.userconcentratedpod_firstfit.toString())) { + } else if (globalValue.equals("userconcentratedpod_firstfit")) { plannerName = "UserConcentratedPodPlanner"; - } else if (globalValue.equals(DeploymentPlanner.AllocationAlgorithm.userconcentratedpod_random.toString())) { + } else if (globalValue.equals("userconcentratedpod_random")) { plannerName = "UserConcentratedPodPlanner"; } else if (globalValue.equals(DeploymentPlanner.AllocationAlgorithm.userdispersing.toString())) { plannerName = "UserDispersingPlanner"; @@ -1947,7 +1947,7 @@ private void migrateS3ToImageStore(Connection conn) { Map detailMap = new HashMap(); detailMap.put(ApiConstants.S3_ACCESS_KEY, s3_accesskey); - detailMap.put(ApiConstants.S3_SECRET_KEY, s3_secretkey); + detailMap.put(ApiConstants.SECRET_KEY, s3_secretkey); detailMap.put(ApiConstants.S3_BUCKET_NAME, s3_bucket); detailMap.put(ApiConstants.S3_END_POINT, s3_endpoint); detailMap.put(ApiConstants.S3_HTTPS_FLAG, String.valueOf(s3_https)); diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41500to41510.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41500to41510.java index a6b77813fa15..c7295414326d 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41500to41510.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41500to41510.java @@ -74,7 +74,7 @@ public void performDataMigration(Connection conn) { @Override @SuppressWarnings("serial") public void updateSystemVmTemplates(final Connection conn) { - logger.debug("Updating System Vm template IDs"); + logger.debug("Updating System VM Template IDs"); final Set hypervisorsListInUse = new HashSet(); try (PreparedStatement pstmt = conn.prepareStatement("select distinct(hypervisor_type) from `cloud`.`cluster` where removed is null"); ResultSet rs = pstmt.executeQuery()) { while (rs.next()) { @@ -153,8 +153,8 @@ public void updateSystemVmTemplates(final Connection conn) { templateId = rs.getLong(1); } } catch (final SQLException e) { - logger.error("updateSystemVmTemplates: Exception caught while getting ids of templates: " + e.getMessage()); - throw new CloudRuntimeException("updateSystemVmTemplates: Exception caught while getting ids of templates", e); + logger.error("updateSystemVmTemplates: Exception caught while getting IDs of Templates: " + e.getMessage()); + throw new CloudRuntimeException("updateSystemVmTemplates: Exception caught while getting IDs of Templates", e); } // change template type to SYSTEM @@ -163,8 +163,8 @@ public void updateSystemVmTemplates(final Connection conn) { templ_type_pstmt.setLong(1, templateId); templ_type_pstmt.executeUpdate(); } catch (final SQLException e) { - logger.error("updateSystemVmTemplates:Exception while updating template with id " + templateId + " to be marked as 'system': " + e.getMessage()); - throw new CloudRuntimeException("updateSystemVmTemplates:Exception while updating template with id " + templateId + " to be marked as 'system'", e); + logger.error("updateSystemVmTemplates:Exception while updating Template with ID: " + templateId + " to be marked as 'system': " + e.getMessage()); + throw new CloudRuntimeException("updateSystemVmTemplates:Exception while updating Template with ID: " + templateId + " to be marked as 'system'", e); } // update template ID of system Vms try (PreparedStatement update_templ_id_pstmt = conn @@ -173,9 +173,9 @@ public void updateSystemVmTemplates(final Connection conn) { update_templ_id_pstmt.setString(2, hypervisorAndTemplateName.getKey().toString()); update_templ_id_pstmt.executeUpdate(); } catch (final Exception e) { - logger.error("updateSystemVmTemplates:Exception while setting template for " + hypervisorAndTemplateName.getKey().toString() + " to " + templateId + logger.error("updateSystemVmTemplates:Exception while setting Template for " + hypervisorAndTemplateName.getKey().toString() + " to " + templateId + ": " + e.getMessage()); - throw new CloudRuntimeException("updateSystemVmTemplates:Exception while setting template for " + hypervisorAndTemplateName.getKey().toString() + " to " + throw new CloudRuntimeException("updateSystemVmTemplates:Exception while setting Template for " + hypervisorAndTemplateName.getKey().toString() + " to " + templateId, e); } @@ -225,8 +225,8 @@ public void updateSystemVmTemplates(final Connection conn) { } } } catch (final SQLException e) { - logger.error("updateSystemVmTemplates:Exception while getting ids of templates: " + e.getMessage()); - throw new CloudRuntimeException("updateSystemVmTemplates:Exception while getting ids of templates", e); + logger.error("updateSystemVmTemplates:Exception while getting IDs of Templates: " + e.getMessage()); + throw new CloudRuntimeException("updateSystemVmTemplates:Exception while getting IDs of Templates", e); } } logger.debug("Updating System Vm Template IDs Complete"); diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41520to41600.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41520to41600.java index 76227d434173..f13e0ffb1169 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41520to41600.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41520to41600.java @@ -94,7 +94,7 @@ private void checkAndPersistAnnotationPermissions(Connection conn, RoleType role } private void generateUuidForExistingSshKeyPairs(Connection conn) { - logger.debug("Generating uuid for existing ssh key-pairs"); + logger.debug("Generating UUID for existing SSH key-pairs"); try { PreparedStatement pstmt = conn.prepareStatement("SELECT id FROM `cloud`.`ssh_keypairs` WHERE uuid is null"); ResultSet rs = pstmt.executeQuery(); @@ -110,9 +110,9 @@ private void generateUuidForExistingSshKeyPairs(Connection conn) { if (!pstmt.isClosed()) { pstmt.close(); } - logger.debug("Successfully generated uuid for existing ssh key-pairs"); + logger.debug("Successfully generated UUID for existing SSH key-pairs"); } catch (SQLException e) { - String errMsg = "Exception while generating uuid for existing ssh key-pairs: " + e.getMessage(); + String errMsg = "Exception while generating UUID for existing SSh key-pairs: " + e.getMessage(); logger.error(errMsg, e); throw new CloudRuntimeException(errMsg, e); } @@ -125,7 +125,7 @@ private void initSystemVmTemplateRegistration() { @Override @SuppressWarnings("serial") public void updateSystemVmTemplates(final Connection conn) { - logger.debug("Updating System Vm template IDs"); + logger.debug("Updating System VM Template IDs"); initSystemVmTemplateRegistration(); try { systemVmTemplateRegistration.updateSystemVmTemplates(conn); diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41600to41610.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41600to41610.java index 3208b4ad8f97..07446a64a8c9 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41600to41610.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41600to41610.java @@ -75,12 +75,12 @@ private void initSystemVmTemplateRegistration() { @Override public void updateSystemVmTemplates(Connection conn) { - logger.debug("Updating System Vm template IDs"); + logger.debug("Updating System VM Template IDs"); initSystemVmTemplateRegistration(); 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)"); } } } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41610to41700.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41610to41700.java index 0a0ab0b9f5a9..10404deac924 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41610to41700.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41610to41700.java @@ -80,12 +80,12 @@ private void initSystemVmTemplateRegistration() { @Override public void updateSystemVmTemplates(Connection conn) { - logger.debug("Updating System Vm template IDs"); + logger.debug("Updating System VM Template IDs"); initSystemVmTemplateRegistration(); 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)"); } } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41700to41710.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41700to41710.java index 266401e0c31c..1f0d7c49a054 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41700to41710.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41700to41710.java @@ -90,12 +90,12 @@ private void initSystemVmTemplateRegistration() { @Override public void updateSystemVmTemplates(Connection conn) { - logger.debug("Updating System Vm template IDs"); + logger.debug("Updating System VM Template IDs"); initSystemVmTemplateRegistration(); try { systemVmTemplateRegistration.updateSystemVmTemplates(conn); } catch (Exception e) { - throw new CloudRuntimeException("Failed to find / register SystemVM template(s)"); + throw new CloudRuntimeException("Failed to find / register System VM Template(s)"); } } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41710to41720.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41710to41720.java index 9854268c1ff1..2e17fa297c27 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41710to41720.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41710to41720.java @@ -62,12 +62,12 @@ private void initSystemVmTemplateRegistration() { @Override public void updateSystemVmTemplates(Connection conn) { - logger.debug("Updating System Vm template IDs"); + logger.debug("Updating System VM Template IDs"); initSystemVmTemplateRegistration(); try { systemVmTemplateRegistration.updateSystemVmTemplates(conn); } catch (Exception e) { - throw new CloudRuntimeException("Failed to find / register SystemVM template(s)"); + throw new CloudRuntimeException("Failed to find / register System VM Template(s)"); } } } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41720to41800.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41720to41800.java index 6a90396deb0b..2f15b85af8e3 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41720to41800.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41720to41800.java @@ -99,12 +99,12 @@ private void initSystemVmTemplateRegistration() { @Override public void updateSystemVmTemplates(Connection conn) { - logger.debug("Updating System Vm template IDs"); + logger.debug("Updating System VM Template IDs"); initSystemVmTemplateRegistration(); try { systemVmTemplateRegistration.updateSystemVmTemplates(conn); } catch (Exception e) { - throw new CloudRuntimeException("Failed to find / register SystemVM template(s)"); + throw new CloudRuntimeException("Failed to find / register System VM Template(s)"); } } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41810to41900.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41810to41900.java index e2b1ae1399b6..35e706595ec1 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41810to41900.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41810to41900.java @@ -95,12 +95,12 @@ private void initSystemVmTemplateRegistration() { @Override public void updateSystemVmTemplates(Connection conn) { - logger.debug("Updating System Vm template IDs"); + logger.debug("Updating System VM Template IDs"); initSystemVmTemplateRegistration(); try { systemVmTemplateRegistration.updateSystemVmTemplates(conn); } catch (Exception e) { - throw new CloudRuntimeException("Failed to find / register SystemVM template(s)"); + throw new CloudRuntimeException("Failed to find / register System VM Template(s)"); } } @@ -159,7 +159,7 @@ private void modifyDateColumnNameAndCreateNewOne(Connection conn) { try (PreparedStatement pstmt = conn.prepareStatement(createNewColumn)) { pstmt.execute(); } catch (SQLException e) { - String message = String.format("Unable to crate new backups' column date due to [%s].", e.getMessage()); + String message = String.format("Unable to create new backups' column date due to [%s].", e.getMessage()); logger.error(message, e); throw new CloudRuntimeException(message, e); } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42020to42030.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42020to42030.java new file mode 100644 index 000000000000..e0aa38a717b2 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42020to42030.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 com.cloud.upgrade.dao; + +import java.io.InputStream; +import java.sql.Connection; + +import com.cloud.utils.exception.CloudRuntimeException; + +public class Upgrade42020to42030 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate { + + @Override + public String[] getUpgradableVersionRange() { + return new String[]{"4.20.2.0", "4.20.3.0"}; + } + + @Override + public String getUpgradedVersion() { + return "4.20.3.0"; + } + + @Override + public boolean supportsRollingUpgrade() { + return false; + } + + @Override + public InputStream[] getPrepareScripts() { + final String scriptFile = "META-INF/db/schema-42020to42030.sql"; + final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile); + if (script == null) { + throw new CloudRuntimeException("Unable to find " + scriptFile); + } + + return new InputStream[] {script}; + } + + @Override + public void performDataMigration(Connection conn) { + } + + @Override + public InputStream[] getCleanupScripts() { + return null; + } +} diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42030to42040.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42030to42040.java new file mode 100644 index 000000000000..90f69a87cb03 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42030to42040.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 com.cloud.upgrade.dao; + +import java.io.InputStream; +import java.sql.Connection; +import java.util.ArrayList; +import java.util.List; + +public class Upgrade42030to42040 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate { + + @Override + public String[] getUpgradableVersionRange() { + return new String[]{"4.20.3.0", "4.20.4.0"}; + } + + @Override + public String getUpgradedVersion() { + return "4.20.4.0"; + } + + @Override + public boolean supportsRollingUpgrade() { + return false; + } + + @Override + public InputStream[] getPrepareScripts() { + return null; + } + + @Override + public void performDataMigration(Connection conn) { + final List indexList = new ArrayList(); + logger.debug("Dropping index vm_instance_id from usage_vm_instance table if it exists"); + indexList.add("vm_instance_id"); + DbUpgradeUtils.dropKeysIfExist(conn, "cloud_usage.usage_vm_instance", indexList, false); + } + + @Override + public InputStream[] getCleanupScripts() { + return null; + } +} diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42010to42100.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42040to42100.java similarity index 97% rename from engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42010to42100.java rename to engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42040to42100.java index 786ee5afbc8e..64ac2c90bc07 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42010to42100.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42040to42100.java @@ -32,12 +32,12 @@ import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.exception.CloudRuntimeException; -public class Upgrade42010to42100 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate { +public class Upgrade42040to42100 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate { private SystemVmTemplateRegistration systemVmTemplateRegistration; @Override public String[] getUpgradableVersionRange() { - return new String[] {"4.20.1.0", "4.21.0.0"}; + return new String[] {"4.20.4.0", "4.21.0.0"}; } @Override @@ -52,7 +52,7 @@ public boolean supportsRollingUpgrade() { @Override public InputStream[] getPrepareScripts() { - final String scriptFile = "META-INF/db/schema-42010to42100.sql"; + final String scriptFile = "META-INF/db/schema-42040to42100.sql"; final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile); if (script == null) { throw new CloudRuntimeException("Unable to find " + scriptFile); @@ -69,7 +69,7 @@ public void performDataMigration(Connection conn) { @Override public InputStream[] getCleanupScripts() { - final String scriptFile = "META-INF/db/schema-42010to42100-cleanup.sql"; + final String scriptFile = "META-INF/db/schema-42040to42100-cleanup.sql"; final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile); if (script == null) { throw new CloudRuntimeException("Unable to find " + scriptFile); 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 index c2cfd02c15cf..5138d51bb1b6 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42100to42200.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42100to42200.java @@ -16,6 +16,14 @@ // under the License. package com.cloud.upgrade.dao; +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; + public class Upgrade42100to42200 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate { @Override @@ -27,4 +35,69 @@ public String[] getUpgradableVersionRange() { public String getUpgradedVersion() { return "4.22.0.0"; } + + @Override + public InputStream[] getPrepareScripts() { + final String scriptFile = "META-INF/db/schema-42100to42200.sql"; + final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile); + if (script == null) { + throw new CloudRuntimeException("Unable to find " + scriptFile); + } + + return new InputStream[] {script}; + } + + @Override + public void performDataMigration(Connection conn) { + updateSnapshotPolicyOwnership(conn); + updateBackupScheduleOwnership(conn); + } + + protected void updateSnapshotPolicyOwnership(Connection conn) { + // set account_id and domain_id in snapshot_policy table from volume table + String selectSql = "SELECT sp.id, v.account_id, v.domain_id FROM snapshot_policy sp, volumes v WHERE sp.volume_id = v.id AND (sp.account_id IS NULL AND sp.domain_id IS NULL)"; + String updateSql = "UPDATE snapshot_policy SET account_id = ?, domain_id = ? WHERE id = ?"; + + try (PreparedStatement selectPstmt = conn.prepareStatement(selectSql); + ResultSet rs = selectPstmt.executeQuery(); + PreparedStatement updatePstmt = conn.prepareStatement(updateSql)) { + + while (rs.next()) { + long policyId = rs.getLong(1); + long accountId = rs.getLong(2); + long domainId = rs.getLong(3); + + updatePstmt.setLong(1, accountId); + updatePstmt.setLong(2, domainId); + updatePstmt.setLong(3, policyId); + updatePstmt.executeUpdate(); + } + } catch (SQLException e) { + throw new CloudRuntimeException("Unable to update snapshot_policy table with account_id and domain_id", e); + } + } + + protected void updateBackupScheduleOwnership(Connection conn) { + // Set account_id and domain_id in backup_schedule table from vm_instance table + String selectSql = "SELECT bs.id, vm.account_id, vm.domain_id FROM backup_schedule bs, vm_instance vm WHERE bs.vm_id = vm.id AND (bs.account_id IS NULL AND bs.domain_id IS NULL)"; + String updateSql = "UPDATE backup_schedule SET account_id = ?, domain_id = ? WHERE id = ?"; + + try (PreparedStatement selectPstmt = conn.prepareStatement(selectSql); + ResultSet rs = selectPstmt.executeQuery(); + PreparedStatement updatePstmt = conn.prepareStatement(updateSql)) { + + while (rs.next()) { + long scheduleId = rs.getLong(1); + long accountId = rs.getLong(2); + long domainId = rs.getLong(3); + + updatePstmt.setLong(1, accountId); + updatePstmt.setLong(2, domainId); + updatePstmt.setLong(3, scheduleId); + updatePstmt.executeUpdate(); + } + } catch (SQLException e) { + throw new CloudRuntimeException("Unable to update backup_schedule table with account_id and domain_id", e); + } + } } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42200to42210.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42200to42210.java new file mode 100644 index 000000000000..813351d75341 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42200to42210.java @@ -0,0 +1,81 @@ +// 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; + +import org.apache.cloudstack.vm.UnmanagedVMsManager; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.List; + +public class Upgrade42200to42210 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate { + + @Override + public String[] getUpgradableVersionRange() { + return new String[] {"4.22.0.0", "4.22.1.0"}; + } + + @Override + public String getUpgradedVersion() { + return "4.22.1.0"; + } + + @Override + public void performDataMigration(Connection conn) { + removeDuplicateKVMImportTemplates(conn); + } + + private void removeDuplicateKVMImportTemplates(Connection conn) { + List templateIds = new ArrayList<>(); + try (PreparedStatement selectStmt = conn.prepareStatement(String.format("SELECT id FROM cloud.vm_template WHERE name='%s' ORDER BY id ASC", UnmanagedVMsManager.KVM_VM_IMPORT_DEFAULT_TEMPLATE_NAME))) { + ResultSet rs = selectStmt.executeQuery(); + while (rs.next()) { + templateIds.add(rs.getLong(1)); + } + + if (templateIds.size() <= 1) { + return; + } + + logger.info("Removing duplicate template " + UnmanagedVMsManager.KVM_VM_IMPORT_DEFAULT_TEMPLATE_NAME + " entries"); + Long firstTemplateId = templateIds.get(0); + + String updateTemplateSql = "UPDATE cloud.vm_instance SET vm_template_id = ? WHERE vm_template_id = ?"; + String deleteTemplateSql = "DELETE FROM cloud.vm_template WHERE id = ?"; + + try (PreparedStatement updateTemplateStmt = conn.prepareStatement(updateTemplateSql); + PreparedStatement deleteTemplateStmt = conn.prepareStatement(deleteTemplateSql)) { + for (int i = 1; i < templateIds.size(); i++) { + Long duplicateTemplateId = templateIds.get(i); + + // Update VM references + updateTemplateStmt.setLong(1, firstTemplateId); + updateTemplateStmt.setLong(2, duplicateTemplateId); + updateTemplateStmt.executeUpdate(); + + // Delete duplicate dummy template + deleteTemplateStmt.setLong(1, duplicateTemplateId); + deleteTemplateStmt.executeUpdate(); + } + } + } catch (Exception e) { + logger.warn("Failed to remove duplicate template " + UnmanagedVMsManager.KVM_VM_IMPORT_DEFAULT_TEMPLATE_NAME + " entries", e); + } + } +} diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42210to42300.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42210to42300.java new file mode 100644 index 000000000000..b60a52bba88b --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42210to42300.java @@ -0,0 +1,102 @@ +// 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; + +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.List; + +import com.cloud.utils.crypt.DBEncryptionUtil; +import com.cloud.utils.exception.CloudRuntimeException; + +public class Upgrade42210to42300 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate { + + @Override + public String[] getUpgradableVersionRange() { + return new String[]{"4.22.1.0", "4.23.0.0"}; + } + + @Override + public String getUpgradedVersion() { + return "4.23.0.0"; + } + + @Override + public InputStream[] getPrepareScripts() { + final String scriptFile = "META-INF/db/schema-42210to42300.sql"; + final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile); + if (script == null) { + throw new CloudRuntimeException("Unable to find " + scriptFile); + } + + return new InputStream[] {script}; + } + + @Override + public void performDataMigration(Connection conn) { + unhideJsInterpretationEnabled(conn); + dropUsageVmInstanceIndex(conn); + } + + protected void unhideJsInterpretationEnabled(Connection conn) { + String value = getJsInterpretationEnabled(conn); + if (value != null) { + updateJsInterpretationEnabledFields(conn, value); + } + } + + protected String getJsInterpretationEnabled(Connection conn) { + String query = "SELECT value FROM cloud.configuration WHERE name = 'js.interpretation.enabled' AND category = 'Hidden';"; + + try (PreparedStatement pstmt = conn.prepareStatement(query)) { + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) { + return rs.getString("value"); + } + logger.debug("Unable to retrieve value of hidden configuration 'js.interpretation.enabled'. The configuration may already be unhidden."); + return null; + } catch (SQLException e) { + throw new CloudRuntimeException("Error while retrieving value of hidden configuration 'js.interpretation.enabled'.", e); + } + } + + protected void updateJsInterpretationEnabledFields(Connection conn, String encryptedValue) { + String query = "UPDATE cloud.configuration SET value = ?, category = 'System', component = 'JsInterpreter', is_dynamic = 1 WHERE name = 'js.interpretation.enabled';"; + + try (PreparedStatement pstmt = conn.prepareStatement(query)) { + String decryptedValue = DBEncryptionUtil.decrypt(encryptedValue); + logger.info("Updating setting 'js.interpretation.enabled' to decrypted value [{}], category 'System', component 'JsInterpreter', and is_dynamic '1'.", decryptedValue); + pstmt.setString(1, decryptedValue); + pstmt.executeUpdate(); + } catch (SQLException e) { + throw new CloudRuntimeException("Error while unhiding configuration 'js.interpretation.enabled'.", e); + } catch (CloudRuntimeException e) { + logger.warn("Error while decrypting configuration 'js.interpretation.enabled'. The configuration may already be decrypted."); + } + } + + private void dropUsageVmInstanceIndex(Connection conn) { + final List indexList = new ArrayList<>(); + logger.debug("Dropping index vm_instance_id from usage_vm_instance table if it exists"); + indexList.add("vm_instance_id"); + DbUpgradeUtils.dropKeysIfExist(conn, "cloud_usage.usage_vm_instance", indexList, false); + } +} diff --git a/engine/schema/src/main/java/com/cloud/usage/UsageVolumeVO.java b/engine/schema/src/main/java/com/cloud/usage/UsageVolumeVO.java index 96abd2d69c08..6d5315e33464 100644 --- a/engine/schema/src/main/java/com/cloud/usage/UsageVolumeVO.java +++ b/engine/schema/src/main/java/com/cloud/usage/UsageVolumeVO.java @@ -59,6 +59,9 @@ public class UsageVolumeVO implements InternalIdentity { @Column(name = "size") private long size; + @Column(name = "vm_id") + private Long vmId; + @Column(name = "created") @Temporal(value = TemporalType.TIMESTAMP) private Date created = null; @@ -70,13 +73,14 @@ public class UsageVolumeVO implements InternalIdentity { protected UsageVolumeVO() { } - public UsageVolumeVO(long id, long zoneId, long accountId, long domainId, Long diskOfferingId, Long templateId, long size, Date created, Date deleted) { + public UsageVolumeVO(long id, long zoneId, long accountId, long domainId, Long diskOfferingId, Long templateId, Long vmId, long size, Date created, Date deleted) { this.volumeId = id; this.zoneId = zoneId; this.accountId = accountId; this.domainId = domainId; this.diskOfferingId = diskOfferingId; this.templateId = templateId; + this.vmId = vmId; this.size = size; this.created = created; this.deleted = deleted; @@ -126,4 +130,12 @@ public void setDeleted(Date deleted) { public long getVolumeId() { return volumeId; } + + public Long getVmId() { + return vmId; + } + + public void setVmId(Long vmId) { + this.vmId = vmId; + } } 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 e5b46b02a591..3f852b0cfb5a 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 @@ -68,7 +68,7 @@ public void removeUsage(Long accountId, Long vmId, Long backupOfferingId, Date e pstmt.setString(1, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), eventDate)); pstmt.setLong(2, accountId); pstmt.setLong(3, vmId); - pstmt.setLong(3, backupOfferingId); + pstmt.setLong(4, backupOfferingId); pstmt.executeUpdate(); } } catch (SQLException e) { diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageDao.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageDao.java index ea490e60f9e7..b9d8d9c05366 100644 --- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageDao.java +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageDao.java @@ -62,7 +62,7 @@ public interface UsageDao extends GenericDao { void saveUsageRecords(List usageRecords); - void removeOldUsageRecords(int days); + void expungeAllOlderThan(int days, long limitPerQuery); UsageVO persistUsage(final UsageVO usage); diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageDaoImpl.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageDaoImpl.java index fb925a285136..2d99c78fad18 100644 --- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageDaoImpl.java @@ -26,15 +26,16 @@ import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.QueryBuilder; +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.TransactionCallbackNoReturn; import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.db.TransactionStatus; import com.cloud.utils.exception.CloudRuntimeException; import org.apache.cloudstack.acl.RoleType; +import org.apache.commons.lang3.time.DateUtils; import org.springframework.stereotype.Component; import java.sql.PreparedStatement; @@ -51,8 +52,7 @@ public class UsageDaoImpl extends GenericDaoBase implements UsageDao { private static final String DELETE_ALL = "DELETE FROM cloud_usage"; private static final String DELETE_ALL_BY_ACCOUNTID = "DELETE FROM cloud_usage WHERE account_id = ?"; - private static final String DELETE_ALL_BY_INTERVAL = "DELETE FROM cloud_usage WHERE end_date < DATE_SUB(CURRENT_DATE(), INTERVAL ? DAY)"; - private static final String INSERT_ACCOUNT = "INSERT INTO cloud_usage.account (id, account_name, type, role_id, domain_id, removed, cleanup_needed) VALUES (?,?,?,?,?,?,?)"; + private static final String INSERT_ACCOUNT = "INSERT INTO cloud_usage.account (id, account_name, uuid, type, role_id, domain_id, removed, cleanup_needed) VALUES (?,?,?,?,?,?,?,?)"; private static final String INSERT_USER_STATS = "INSERT INTO cloud_usage.user_statistics (id, data_center_id, account_id, public_ip_address, device_id, device_type, network_id, net_bytes_received," + " net_bytes_sent, current_bytes_received, current_bytes_sent, agg_bytes_received, agg_bytes_sent) VALUES (?,?,?,?,?,?,?,?,?,?, ?, ?, ?)"; @@ -88,8 +88,12 @@ public class UsageDaoImpl extends GenericDaoBase implements Usage private static final String UPDATE_BUCKET_STATS = "UPDATE cloud_usage.bucket_statistics SET size=? WHERE id=?"; + protected SearchBuilder endDateLessThanSearch; public UsageDaoImpl() { + endDateLessThanSearch = createSearchBuilder(); + endDateLessThanSearch.and("endDate", endDateLessThanSearch.entity().getEndDate(), SearchCriteria.Op.LT); + endDateLessThanSearch.done(); } @Override @@ -107,7 +111,7 @@ public void deleteRecordsForAccount(Long accountId) { txn.commit(); } catch (Exception ex) { txn.rollback(); - logger.error("error retrieving usage vm instances for account id: " + accountId, ex); + logger.error("Error retrieving usage Instances for Account ID: " + accountId, ex); } finally { txn.close(); } @@ -129,25 +133,26 @@ public void saveAccounts(List accounts) { for (AccountVO acct : accounts) { pstmt.setLong(1, acct.getId()); pstmt.setString(2, acct.getAccountName()); - pstmt.setInt(3, acct.getType().ordinal()); + pstmt.setString(3, acct.getUuid()); + pstmt.setInt(4, acct.getType().ordinal()); //prevent autoboxing NPE by defaulting to User role if(acct.getRoleId() == null){ - pstmt.setLong(4, RoleType.User.getId()); + pstmt.setLong(5, RoleType.User.getId()); }else{ - pstmt.setLong(4, acct.getRoleId()); + pstmt.setLong(5, acct.getRoleId()); } - pstmt.setLong(5, acct.getDomainId()); + pstmt.setLong(6, acct.getDomainId()); Date removed = acct.getRemoved(); if (removed == null) { - pstmt.setString(6, null); + pstmt.setString(7, null); } else { - pstmt.setString(6, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), acct.getRemoved())); + pstmt.setString(7, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), acct.getRemoved())); } - pstmt.setBoolean(7, acct.getNeedsCleanup()); + pstmt.setBoolean(8, acct.getNeedsCleanup()); pstmt.addBatch(); } @@ -369,7 +374,7 @@ public List listPublicTemplatesByAccount(long accountId) { templateList.add(Long.valueOf(rs.getLong(1))); } } catch (Exception ex) { - logger.error("error listing public templates", ex); + logger.error("Error listing public Templates", ex); } return templateList; } @@ -538,21 +543,20 @@ public void saveUsageRecords(List usageRecords) { } @Override - public void removeOldUsageRecords(int days) { - Transaction.execute(TransactionLegacy.USAGE_DB, new TransactionCallbackNoReturn() { - @Override - public void doInTransactionWithoutResult(TransactionStatus status) { - TransactionLegacy txn = TransactionLegacy.currentTxn(); - PreparedStatement pstmt = null; - try { - pstmt = txn.prepareAutoCloseStatement(DELETE_ALL_BY_INTERVAL); - pstmt.setLong(1, days); - pstmt.executeUpdate(); - } catch (Exception ex) { - logger.error("error removing old cloud_usage records for interval: " + days); - } - } - }); + public void expungeAllOlderThan(int days, long limitPerQuery) { + SearchCriteria sc = endDateLessThanSearch.create(); + + Date limit = DateUtils.addDays(new Date(), -days); + sc.setParameters("endDate", limit); + + TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); + try { + logger.debug("Removing all cloud_usage records older than [{}].", limit); + int totalExpunged = batchExpunge(sc, limitPerQuery); + logger.info("Removed a total of [{}] cloud_usage records older than [{}].", totalExpunged, limit); + } finally { + txn.close(); + } } public UsageVO persistUsage(final UsageVO usage) { 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 d4038d4ceeb4..b22ce69d94ea 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 @@ -28,6 +28,8 @@ public interface UsageJobDao extends GenericDao { UsageJobVO getLastJob(); + UsageJobVO getNextRecurringJob(); + UsageJobVO getNextImmediateJob(); long getLastJobSuccessDateMillis(); 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 44a7d1a8b727..68ad8fa12edd 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 @@ -61,9 +61,6 @@ public long getLastJobSuccessDateMillis() { public void updateJobSuccess(Long jobId, long startMillis, long endMillis, long execTime, boolean success) { TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); try { - txn.start(); - - UsageJobVO job = lockRow(jobId, Boolean.TRUE); UsageJobVO jobForUpdate = createForUpdate(); jobForUpdate.setStartMillis(startMillis); jobForUpdate.setEndMillis(endMillis); @@ -71,11 +68,8 @@ public void updateJobSuccess(Long jobId, long startMillis, long endMillis, long jobForUpdate.setStartDate(new Date(startMillis)); jobForUpdate.setEndDate(new Date(endMillis)); jobForUpdate.setSuccess(success); - update(job.getId(), jobForUpdate); - - txn.commit(); + update(jobId, jobForUpdate); } catch (Exception ex) { - txn.rollback(); logger.error("error updating job success date", ex); throw new CloudRuntimeException(ex.getMessage()); } finally { @@ -156,7 +150,8 @@ public UsageJobVO getLastJob() { return jobs.get(0); } - private UsageJobVO getNextRecurringJob() { + @Override + public UsageJobVO getNextRecurringJob() { Filter filter = new Filter(UsageJobVO.class, "id", false, Long.valueOf(0), Long.valueOf(1)); SearchCriteria sc = createSearchCriteria(); sc.addAnd("endMillis", SearchCriteria.Op.EQ, Long.valueOf(0)); diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageStorageDaoImpl.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageStorageDaoImpl.java index 1da533493997..f863cd1e3a35 100644 --- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageStorageDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageStorageDaoImpl.java @@ -57,6 +57,7 @@ public UsageStorageDaoImpl() { IdSearch.and("accountId", IdSearch.entity().getAccountId(), SearchCriteria.Op.EQ); IdSearch.and("id", IdSearch.entity().getEntityId(), SearchCriteria.Op.EQ); IdSearch.and("type", IdSearch.entity().getStorageType(), SearchCriteria.Op.EQ); + IdSearch.and("deleted", IdSearch.entity().getDeleted(), SearchCriteria.Op.NULL); IdSearch.done(); IdZoneSearch = createSearchBuilder(); @@ -74,6 +75,7 @@ public List listById(long accountId, long id, int type) { sc.setParameters("accountId", accountId); sc.setParameters("id", id); sc.setParameters("type", type); + sc.setParameters("deleted", null); return listBy(sc, null); } diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageVMInstanceDaoImpl.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageVMInstanceDaoImpl.java index 2fd453013bd1..41138bae2bdc 100644 --- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageVMInstanceDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageVMInstanceDaoImpl.java @@ -81,7 +81,7 @@ public void delete(UsageVMInstanceVO instance) { txn.commit(); } catch (Exception ex) { txn.rollback(); - logger.error("error deleting usage vm instance with vmId: " + instance.getVmInstanceId() + ", for account with id: " + instance.getAccountId()); + logger.error("Error deleting usage Instance with vmId: " + instance.getVmInstanceId() + ", for account with id: " + instance.getAccountId()); } finally { txn.close(); } @@ -139,7 +139,7 @@ public List getUsageRecords(long accountId, Date startDate, D usageInstances.add(usageInstance); } } catch (Exception ex) { - logger.error("error retrieving usage vm instances for account id: " + accountId, ex); + logger.error("Error retrieving usage Instances for Account ID: " + accountId, ex); } finally { txn.close(); } diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageVolumeDao.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageVolumeDao.java index 09590b739930..05287240f254 100644 --- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageVolumeDao.java +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageVolumeDao.java @@ -23,9 +23,7 @@ import com.cloud.utils.db.GenericDao; public interface UsageVolumeDao extends GenericDao { - public void removeBy(long userId, long id); - - public void update(UsageVolumeVO usage); - public List getUsageRecords(Long accountId, Long domainId, Date startDate, Date endDate, boolean limit, int page); + + List listByVolumeId(long volumeId, long accountId); } diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageVolumeDaoImpl.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageVolumeDaoImpl.java index 4662a6f26ce8..095070feac1c 100644 --- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageVolumeDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageVolumeDaoImpl.java @@ -18,81 +18,46 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; -import java.sql.SQLException; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.TimeZone; -import com.cloud.exception.CloudException; +import javax.annotation.PostConstruct; + import org.springframework.stereotype.Component; import com.cloud.usage.UsageVolumeVO; 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; @Component public class UsageVolumeDaoImpl extends GenericDaoBase implements UsageVolumeDao { - protected static final String REMOVE_BY_USERID_VOLID = "DELETE FROM usage_volume WHERE account_id = ? AND volume_id = ?"; - protected static final String UPDATE_DELETED = "UPDATE usage_volume SET deleted = ? WHERE account_id = ? AND volume_id = ? and deleted IS NULL"; - protected static final String GET_USAGE_RECORDS_BY_ACCOUNT = "SELECT volume_id, zone_id, account_id, domain_id, disk_offering_id, template_id, size, created, deleted " + protected static final String GET_USAGE_RECORDS_BY_ACCOUNT = "SELECT volume_id, zone_id, account_id, domain_id, disk_offering_id, template_id, vm_id, size, created, deleted " + "FROM usage_volume " + "WHERE account_id = ? AND ((deleted IS NULL) OR (created BETWEEN ? AND ?) OR " + " (deleted BETWEEN ? AND ?) OR ((created <= ?) AND (deleted >= ?)))"; - protected static final String GET_USAGE_RECORDS_BY_DOMAIN = "SELECT volume_id, zone_id, account_id, domain_id, disk_offering_id, template_id, size, created, deleted " + protected static final String GET_USAGE_RECORDS_BY_DOMAIN = "SELECT volume_id, zone_id, account_id, domain_id, disk_offering_id, template_id, vm_id, size, created, deleted " + "FROM usage_volume " + "WHERE domain_id = ? AND ((deleted IS NULL) OR (created BETWEEN ? AND ?) OR " + " (deleted BETWEEN ? AND ?) OR ((created <= ?) AND (deleted >= ?)))"; - protected static final String GET_ALL_USAGE_RECORDS = "SELECT volume_id, zone_id, account_id, domain_id, disk_offering_id, template_id, size, created, deleted " + protected static final String GET_ALL_USAGE_RECORDS = "SELECT volume_id, zone_id, account_id, domain_id, disk_offering_id, template_id, vm_id, size, created, deleted " + "FROM usage_volume " + "WHERE (deleted IS NULL) OR (created BETWEEN ? AND ?) OR " + " (deleted BETWEEN ? AND ?) OR ((created <= ?) AND (deleted >= ?))"; + private SearchBuilder volumeSearch; public UsageVolumeDaoImpl() { } - @Override - public void removeBy(long accountId, long volId) { - TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); - try { - txn.start(); - try(PreparedStatement pstmt = txn.prepareStatement(REMOVE_BY_USERID_VOLID);) { - if (pstmt != null) { - pstmt.setLong(1, accountId); - pstmt.setLong(2, volId); - pstmt.executeUpdate(); - } - }catch (SQLException e) { - throw new CloudException("Error removing usageVolumeVO:"+e.getMessage(), e); - } - txn.commit(); - } catch (Exception e) { - txn.rollback(); - logger.warn("Error removing usageVolumeVO:"+e.getMessage(), e); - } finally { - txn.close(); - } - } - - @Override - public void update(UsageVolumeVO usage) { - TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); - PreparedStatement pstmt = null; - try { - txn.start(); - if (usage.getDeleted() != null) { - pstmt = txn.prepareAutoCloseStatement(UPDATE_DELETED); - pstmt.setString(1, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), usage.getDeleted())); - pstmt.setLong(2, usage.getAccountId()); - pstmt.setLong(3, usage.getVolumeId()); - pstmt.executeUpdate(); - } - txn.commit(); - } catch (Exception e) { - txn.rollback(); - logger.warn("Error updating UsageVolumeVO", e); - } finally { - txn.close(); - } + @PostConstruct + protected void init() { + volumeSearch = createSearchBuilder(); + volumeSearch.and("accountId", volumeSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + volumeSearch.and("volumeId", volumeSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); + volumeSearch.and("deleted", volumeSearch.entity().getDeleted(), SearchCriteria.Op.NULL); + volumeSearch.done(); } @Override @@ -150,11 +115,15 @@ public List getUsageRecords(Long accountId, Long domainId, Date s if (tId == 0) { tId = null; } - long size = Long.valueOf(rs.getLong(7)); + Long vmId = Long.valueOf(rs.getLong(7)); + if (vmId == 0) { + vmId = null; + } + long size = Long.valueOf(rs.getLong(8)); Date createdDate = null; Date deletedDate = null; - String createdTS = rs.getString(8); - String deletedTS = rs.getString(9); + String createdTS = rs.getString(9); + String deletedTS = rs.getString(10); if (createdTS != null) { createdDate = DateUtil.parseDateString(s_gmtTimeZone, createdTS); @@ -163,7 +132,7 @@ public List getUsageRecords(Long accountId, Long domainId, Date s deletedDate = DateUtil.parseDateString(s_gmtTimeZone, deletedTS); } - usageRecords.add(new UsageVolumeVO(vId, zoneId, acctId, dId, doId, tId, size, createdDate, deletedDate)); + usageRecords.add(new UsageVolumeVO(vId, zoneId, acctId, dId, doId, tId, vmId, size, createdDate, deletedDate)); } } catch (Exception e) { txn.rollback(); @@ -174,4 +143,13 @@ public List getUsageRecords(Long accountId, Long domainId, Date s return usageRecords; } + + @Override + public List listByVolumeId(long volumeId, long accountId) { + SearchCriteria sc = volumeSearch.create(); + sc.setParameters("accountId", accountId); + sc.setParameters("volumeId", volumeId); + sc.setParameters("deleted", null); + return listBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/user/UserAccountVO.java b/engine/schema/src/main/java/com/cloud/user/UserAccountVO.java index e4fcbad6b02f..7345eeb48539 100644 --- a/engine/schema/src/main/java/com/cloud/user/UserAccountVO.java +++ b/engine/schema/src/main/java/com/cloud/user/UserAccountVO.java @@ -36,7 +36,6 @@ import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.commons.lang3.StringUtils; -import com.cloud.utils.db.Encrypt; import com.cloud.utils.db.GenericDao; @Entity @@ -69,13 +68,6 @@ public class UserAccountVO implements UserAccount, InternalIdentity { @Column(name = "state") private String state; - @Column(name = "api_key") - private String apiKey = null; - - @Encrypt - @Column(name = "secret_key") - private String secretKey = null; - @Column(name = GenericDao.CREATED_COLUMN) private Date created; @@ -203,33 +195,11 @@ public void setState(String state) { this.state = state; } - @Override - public String getApiKey() { - return apiKey; - } - - public void setApiKey(String apiKey) { - this.apiKey = apiKey; - } - - @Override - public String getSecretKey() { - return secretKey; - } - - public void setSecretKey(String secretKey) { - this.secretKey = secretKey; - } - @Override public Date getCreated() { return created; } -// public void setCreated(Date created) { -// this.created = created; -// } - @Override public Date getRemoved() { return removed; diff --git a/engine/schema/src/main/java/com/cloud/user/UserVO.java b/engine/schema/src/main/java/com/cloud/user/UserVO.java index 6e355e102e6c..1b89bc215cf7 100644 --- a/engine/schema/src/main/java/com/cloud/user/UserVO.java +++ b/engine/schema/src/main/java/com/cloud/user/UserVO.java @@ -33,7 +33,6 @@ import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import com.cloud.user.Account.State; -import com.cloud.utils.db.Encrypt; import com.cloud.utils.db.GenericDao; import org.apache.commons.lang3.StringUtils; @@ -71,13 +70,6 @@ public class UserVO implements User, Identity, InternalIdentity { @Enumerated(value = EnumType.STRING) private State state; - @Column(name = "api_key") - private String apiKey = null; - - @Encrypt - @Column(name = "secret_key") - private String secretKey = null; - @Column(name = GenericDao.CREATED_COLUMN) private Date created; @@ -123,8 +115,8 @@ public UserVO() { } public UserVO(long id) { + this(); this.id = id; - this.uuid = UUID.randomUUID().toString(); } public UserVO(long accountId, String username, String password, String firstName, String lastName, String email, String timezone, String uuid, Source source) { @@ -150,8 +142,6 @@ public UserVO(UserVO user) { this.setTimezone(user.getTimezone()); this.setUuid(user.getUuid()); this.setSource(user.getSource()); - this.setApiKey(user.getApiKey()); - this.setSecretKey(user.getSecretKey()); this.setExternalEntity(user.getExternalEntity()); this.setRegistered(user.isRegistered()); this.setRegistrationToken(user.getRegistrationToken()); @@ -243,26 +233,6 @@ public void setState(State state) { this.state = state; } - @Override - public String getApiKey() { - return apiKey; - } - - @Override - public void setApiKey(String apiKey) { - this.apiKey = apiKey; - } - - @Override - public String getSecretKey() { - return secretKey; - } - - @Override - public void setSecretKey(String secretKey) { - this.secretKey = secretKey; - } - @Override public String getTimezone() { if (StringUtils.isEmpty(timezone)) { 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 dae5f3a34677..67b70571cb4c 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 @@ -21,13 +21,11 @@ import com.cloud.user.Account; import com.cloud.user.AccountVO; -import com.cloud.user.User; import com.cloud.utils.Pair; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDao; public interface AccountDao extends GenericDao { - Pair findUserAccountByApiKey(String apiKey); List findAccountsLike(String accountName); 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 f5f95d5da1ff..48b29fac45eb 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,8 +16,6 @@ // under the License. package com.cloud.user.dao; -import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.util.Date; import java.util.List; @@ -27,10 +25,7 @@ import com.cloud.user.Account; import com.cloud.user.Account.State; import com.cloud.user.AccountVO; -import com.cloud.user.User; -import com.cloud.user.UserVO; import com.cloud.utils.Pair; -import com.cloud.utils.crypt.DBEncryptionUtil; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; @@ -38,13 +33,9 @@ import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Func; import com.cloud.utils.db.SearchCriteria.Op; -import com.cloud.utils.db.TransactionLegacy; @Component public class AccountDaoImpl extends GenericDaoBase implements AccountDao { - private static final String FIND_USER_ACCOUNT_BY_API_KEY = "SELECT u.id, u.username, u.account_id, u.secret_key, u.state, u.api_key_access, " - + "a.id, a.account_name, a.type, a.role_id, a.domain_id, a.state, a.api_key_access " + "FROM `cloud`.`user` u, `cloud`.`account` a " - + "WHERE u.account_id = a.id AND u.api_key = ? and u.removed IS NULL"; protected final SearchBuilder AllFieldsSearch; protected final SearchBuilder AccountTypeSearch; @@ -132,51 +123,6 @@ public List findCleanupsForDisabledAccounts() { return listBy(sc); } - @Override - public Pair findUserAccountByApiKey(String apiKey) { - TransactionLegacy txn = TransactionLegacy.currentTxn(); - PreparedStatement pstmt = null; - Pair userAcctPair = null; - try { - String sql = FIND_USER_ACCOUNT_BY_API_KEY; - pstmt = txn.prepareAutoCloseStatement(sql); - pstmt.setString(1, apiKey); - ResultSet rs = pstmt.executeQuery(); - // TODO: make sure we don't have more than 1 result? ApiKey had better be unique - if (rs.next()) { - User u = new UserVO(rs.getLong(1)); - u.setUsername(rs.getString(2)); - u.setAccountId(rs.getLong(3)); - u.setSecretKey(DBEncryptionUtil.decrypt(rs.getString(4))); - u.setState(State.getValueOf(rs.getString(5))); - boolean apiKeyAccess = rs.getBoolean(6); - if (rs.wasNull()) { - u.setApiKeyAccess(null); - } else { - u.setApiKeyAccess(apiKeyAccess); - } - - AccountVO a = new AccountVO(rs.getLong(7)); - a.setAccountName(rs.getString(8)); - a.setType(Account.Type.getFromValue(rs.getInt(9))); - a.setRoleId(rs.getLong(10)); - a.setDomainId(rs.getLong(11)); - a.setState(State.getValueOf(rs.getString(12))); - apiKeyAccess = rs.getBoolean(13); - if (rs.wasNull()) { - a.setApiKeyAccess(null); - } else { - a.setApiKeyAccess(apiKeyAccess); - } - - userAcctPair = new Pair(u, a); - } - } catch (Exception e) { - logger.warn("Exception finding user/acct by api key: " + apiKey, e); - } - return userAcctPair; - } - @Override public List findAccountsLike(String accountName) { return findAccountsLike(accountName, null).first(); @@ -341,11 +287,9 @@ public long getDomainIdForGivenAccountId(long id) { domain_id = account_vo.getDomainId(); } catch (Exception e) { - logger.warn("getDomainIdForGivenAccountId: Exception :" + e.getMessage()); - } - finally { - return domain_id; + logger.warn("Can not get DomainId for the given AccountId; exception message : {}", e.getMessage()); } + return domain_id; } @Override diff --git a/engine/schema/src/main/java/com/cloud/user/dao/UserAccountDao.java b/engine/schema/src/main/java/com/cloud/user/dao/UserAccountDao.java index de3b769571e9..e377bbab94ed 100644 --- a/engine/schema/src/main/java/com/cloud/user/dao/UserAccountDao.java +++ b/engine/schema/src/main/java/com/cloud/user/dao/UserAccountDao.java @@ -30,6 +30,4 @@ public interface UserAccountDao extends GenericDao { List getUserAccountByEmail(String email, Long domainId); boolean validateUsernameInDomain(String username, Long domainId); - - UserAccount getUserByApiKey(String apiKey); } diff --git a/engine/schema/src/main/java/com/cloud/user/dao/UserAccountDaoImpl.java b/engine/schema/src/main/java/com/cloud/user/dao/UserAccountDaoImpl.java index c9de9a367eed..28392abbff5c 100644 --- a/engine/schema/src/main/java/com/cloud/user/dao/UserAccountDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/user/dao/UserAccountDaoImpl.java @@ -19,7 +19,6 @@ import com.cloud.user.UserAccount; import com.cloud.user.UserAccountVO; import com.cloud.utils.db.GenericDaoBase; -import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import org.springframework.stereotype.Component; @@ -28,14 +27,6 @@ @Component public class UserAccountDaoImpl extends GenericDaoBase implements UserAccountDao { - protected final SearchBuilder userAccountSearch; - - public UserAccountDaoImpl() { - userAccountSearch = createSearchBuilder(); - userAccountSearch.and("apiKey", userAccountSearch.entity().getApiKey(), SearchCriteria.Op.EQ); - userAccountSearch.done(); - } - @Override public List getAllUsersByNameAndEntity(String username, String entity) { if (username == null) { @@ -79,12 +70,4 @@ public boolean validateUsernameInDomain(String username, Long domainId) { } return false; } - - @Override - public UserAccount getUserByApiKey(String apiKey) { - SearchCriteria sc = userAccountSearch.create(); - sc.setParameters("apiKey", apiKey); - return findOneBy(sc); - } - } diff --git a/engine/schema/src/main/java/com/cloud/user/dao/UserDao.java b/engine/schema/src/main/java/com/cloud/user/dao/UserDao.java index 14b074251508..2e160efb9506 100644 --- a/engine/schema/src/main/java/com/cloud/user/dao/UserDao.java +++ b/engine/schema/src/main/java/com/cloud/user/dao/UserDao.java @@ -37,13 +37,6 @@ public interface UserDao extends GenericDao { List listByAccount(long accountId); - /** - * Finds a user based on the secret key provided. - * @param secretKey - * @return - */ - UserVO findUserBySecretKey(String secretKey); - /** * Finds a user based on the registration token provided. * @param registrationToken diff --git a/engine/schema/src/main/java/com/cloud/user/dao/UserDaoImpl.java b/engine/schema/src/main/java/com/cloud/user/dao/UserDaoImpl.java index 8baf732c2406..de60e48dff8f 100644 --- a/engine/schema/src/main/java/com/cloud/user/dao/UserDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/user/dao/UserDaoImpl.java @@ -65,10 +65,6 @@ protected UserDaoImpl() { UserIdSearch.and("id", UserIdSearch.entity().getId(), SearchCriteria.Op.EQ); UserIdSearch.done(); - SecretKeySearch = createSearchBuilder(); - SecretKeySearch.and("secretKey", SecretKeySearch.entity().getSecretKey(), SearchCriteria.Op.EQ); - SecretKeySearch.done(); - RegistrationTokenSearch = createSearchBuilder(); RegistrationTokenSearch.and("registrationToken", RegistrationTokenSearch.entity().getRegistrationToken(), SearchCriteria.Op.EQ); RegistrationTokenSearch.done(); @@ -121,13 +117,6 @@ public List findUsersLike(String username) { return listBy(sc); } - @Override - public UserVO findUserBySecretKey(String secretKey) { - SearchCriteria sc = SecretKeySearch.create(); - sc.setParameters("secretKey", secretKey); - return findOneBy(sc); - } - @Override public UserVO findUserByRegistrationToken(String registrationToken) { SearchCriteria sc = RegistrationTokenSearch.create(); diff --git a/engine/schema/src/main/java/com/cloud/vm/ImportVMTaskVO.java b/engine/schema/src/main/java/com/cloud/vm/ImportVMTaskVO.java new file mode 100644 index 000000000000..9a8a769f0a56 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/vm/ImportVMTaskVO.java @@ -0,0 +1,270 @@ +// +// 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 org.apache.cloudstack.vm.ImportVmTask; + +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 = "import_vm_task") +public class ImportVMTaskVO implements ImportVmTask { + + public ImportVMTaskVO(long zoneId, long accountId, long userId, String displayName, + String vcenter, String datacenter, String sourceVMName, long convertHostId, long importHostId) { + this.zoneId = zoneId; + this.accountId = accountId; + this.userId = userId; + this.displayName = displayName; + this.vcenter = vcenter; + this.datacenter = datacenter; + this.sourceVMName = sourceVMName; + this.step = Step.Prepare; + this.uuid = UUID.randomUUID().toString(); + this.convertHostId = convertHostId; + this.importHostId = importHostId; + } + + public ImportVMTaskVO() { + } + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "zone_id") + private long zoneId; + + @Column(name = "account_id") + private long accountId; + + @Column(name = "user_id") + private long userId; + + @Column(name = "vm_id") + private Long vmId; + @Column(name = "display_name") + private String displayName; + + @Column(name = "vcenter") + private String vcenter; + + @Column(name = "datacenter") + private String datacenter; + + @Column(name = "source_vm_name") + private String sourceVMName; + + @Column(name = "convert_host_id") + private long convertHostId; + + @Column(name = "import_host_id") + private long importHostId; + + @Column(name = "step") + private Step step; + + @Column(name = "state") + private TaskState state; + + @Column(name = "description") + private String description; + + @Column(name = "duration") + private Long duration; + + @Column(name = "created") + @Temporal(value = TemporalType.TIMESTAMP) + private Date created; + + @Column(name = "updated") + @Temporal(value = TemporalType.TIMESTAMP) + private Date updated; + + @Column(name = "removed") + @Temporal(value = TemporalType.TIMESTAMP) + private Date removed; + + @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; + } + + public long getZoneId() { + return zoneId; + } + + public void setZoneId(long zoneId) { + this.zoneId = zoneId; + } + + public long getAccountId() { + return accountId; + } + + public void setAccountId(long accountId) { + this.accountId = accountId; + } + + public long getUserId() { + return userId; + } + + public void setUserId(long userId) { + this.userId = userId; + } + + public Long getVmId() { + return vmId; + } + + public void setVmId(Long vmId) { + this.vmId = vmId; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public String getVcenter() { + return vcenter; + } + + public void setVcenter(String vcenter) { + this.vcenter = vcenter; + } + + public String getDatacenter() { + return datacenter; + } + + public void setDatacenter(String datacenter) { + this.datacenter = datacenter; + } + + public String getSourceVMName() { + return sourceVMName; + } + + public void setSourceVMName(String sourceVMName) { + this.sourceVMName = sourceVMName; + } + + public long getConvertHostId() { + return convertHostId; + } + + public void setConvertHostId(long convertHostId) { + this.convertHostId = convertHostId; + } + + public long getImportHostId() { + return importHostId; + } + + public void setImportHostId(long importHostId) { + this.importHostId = importHostId; + } + + public Step getStep() { + return step; + } + + public void setStep(Step step) { + this.step = step; + } + + public TaskState getState() { + return state; + } + + public void setState(TaskState state) { + this.state = state; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Long getDuration() { + return duration; + } + + public void setDuration(Long duration) { + this.duration = duration; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public Date getUpdated() { + return updated; + } + + public void setUpdated(Date updated) { + this.updated = updated; + } + + public Date getRemoved() { + return removed; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } +} diff --git a/engine/schema/src/main/java/com/cloud/vm/NicVO.java b/engine/schema/src/main/java/com/cloud/vm/NicVO.java index 6c569e22dd95..65946b8d8210 100644 --- a/engine/schema/src/main/java/com/cloud/vm/NicVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/NicVO.java @@ -131,6 +131,9 @@ protected NicVO() { @Column(name = "mtu") Integer mtu; + @Column(name = "enabled") + boolean enabled; + @Transient transient String nsxLogicalSwitchUuid; @@ -143,6 +146,7 @@ public NicVO(String reserver, Long instanceId, long configurationId, VirtualMach this.networkId = configurationId; this.state = State.Allocated; this.vmType = vmType; + this.enabled = true; } @Override @@ -397,6 +401,14 @@ public void setNsxLogicalSwitchPortUuid(String nsxLogicalSwitchPortUuid) { this.nsxLogicalSwitchPortUuid = nsxLogicalSwitchPortUuid; } + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + @Override public int hashCode() { return new HashCodeBuilder(17, 31).append(id).toHashCode(); diff --git a/engine/schema/src/main/java/com/cloud/vm/VmIsoMapVO.java b/engine/schema/src/main/java/com/cloud/vm/VmIsoMapVO.java new file mode 100644 index 000000000000..f4a3f1168188 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/vm/VmIsoMapVO.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 com.cloud.vm; + +import java.util.Date; + +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 org.apache.cloudstack.api.InternalIdentity; + +@Entity +@Table(name = "vm_iso_map") +public class VmIsoMapVO implements InternalIdentity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "vm_id") + private long vmId; + + @Column(name = "iso_id") + private long isoId; + + @Column(name = "device_seq") + private int deviceSeq; + + @Column(name = "created") + @Temporal(TemporalType.TIMESTAMP) + private Date created; + + public VmIsoMapVO() { + } + + public VmIsoMapVO(long vmId, long isoId, int deviceSeq) { + this.vmId = vmId; + this.isoId = isoId; + this.deviceSeq = deviceSeq; + this.created = new Date(); + } + + @Override + public long getId() { + return id; + } + + public long getVmId() { + return vmId; + } + + public long getIsoId() { + return isoId; + } + + public int getDeviceSeq() { + return deviceSeq; + } + + public Date getCreated() { + return created; + } +} diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/ImportVMTaskDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/ImportVMTaskDao.java new file mode 100644 index 000000000000..a4f0a155da41 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/vm/dao/ImportVMTaskDao.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.vm.dao; + +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; +import com.cloud.vm.ImportVMTaskVO; +import org.apache.cloudstack.vm.ImportVmTask; +import java.util.List; + +public interface ImportVMTaskDao extends GenericDao { + + Pair, Integer> listImportVMTasks(Long zoneId, Long accountId, String vcenter, Long convertHostId, + ImportVmTask.TaskState state, Long startIndex, Long pageSizeVal); +} diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/ImportVMTaskDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/ImportVMTaskDaoImpl.java new file mode 100644 index 000000000000..da9c391af9db --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/vm/dao/ImportVMTaskDaoImpl.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 com.cloud.vm.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 com.cloud.vm.ImportVMTaskVO; +import org.apache.cloudstack.vm.ImportVmTask; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.List; + +@Component +public class ImportVMTaskDaoImpl extends GenericDaoBase implements ImportVMTaskDao { + + private SearchBuilder AllFieldsSearch; + + public ImportVMTaskDaoImpl() { + } + + @PostConstruct + void init() { + AllFieldsSearch = createSearchBuilder(); + AllFieldsSearch.and("zoneId", AllFieldsSearch.entity().getZoneId(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("accountId", AllFieldsSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("vcenter", AllFieldsSearch.entity().getVcenter(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("convertHostId", AllFieldsSearch.entity().getConvertHostId(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("state", AllFieldsSearch.entity().getState(), SearchCriteria.Op.EQ); + AllFieldsSearch.done(); + } + + + @Override + public Pair, Integer> listImportVMTasks(Long zoneId, Long accountId, String vcenter, Long convertHostId, + ImportVmTask.TaskState state, Long startIndex, Long pageSizeVal) { + SearchCriteria sc = AllFieldsSearch.create(); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + if (accountId != null) { + sc.setParameters("accountId", accountId); + } + if (StringUtils.isNotBlank(vcenter)) { + sc.setParameters("vcenter", vcenter); + } + if (convertHostId != null) { + sc.setParameters("convertHostId", convertHostId); + } + if (state != null) { + sc.setParameters("state", state); + } + Filter filter = new Filter(ImportVMTaskVO.class, "created", false, startIndex, pageSizeVal); + return searchAndCount(sc, filter); + } +} 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 70a2558e2d49..ad599053ff0c 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 @@ -26,6 +26,8 @@ public interface NicDao extends GenericDao { List listByVmId(long instanceId); + int countByVmId(long instanceId); + List listByVmIdOrderByDeviceId(long instanceId); List listIpAddressInNetwork(long networkConfigId); @@ -95,7 +97,9 @@ public interface NicDao extends GenericDao { List listByVmIdAndKeyword(long instanceId, String keyword); - NicVO findByMacAddress(String macAddress); + NicVO findByMacAddress(String macAddress, long networkId); + + List listByMacAddresses(List macAddresses); NicVO findByNetworkIdAndMacAddressIncludingRemoved(long networkId, String mac); 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 3618785c1b89..e468b1b9a3bf 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 @@ -18,12 +18,13 @@ import java.net.URI; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import javax.annotation.PostConstruct; import javax.inject.Inject; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.utils.db.Filter; @@ -71,7 +72,7 @@ protected void init() { 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("macAddress", AllFieldsSearch.entity().getMacAddress(), Op.IN); AllFieldsSearch.and("deviceid", AllFieldsSearch.entity().getDeviceId(), Op.EQ); AllFieldsSearch.and("ipv6Gateway", AllFieldsSearch.entity().getIPv6Gateway(), Op.EQ); AllFieldsSearch.and("ipv6Cidr", AllFieldsSearch.entity().getIPv6Cidr(), Op.EQ); @@ -126,6 +127,13 @@ public List listByVmId(long instanceId) { return listBy(sc); } + @Override + public int countByVmId(long instanceId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("instance", instanceId); + return getCount(sc); + } + @Override public List listByVmIdOrderByDeviceId(long instanceId) { SearchCriteria sc = AllFieldsSearch.create(); @@ -420,12 +428,23 @@ public List listByVmIdAndKeyword(long instanceId, String keyword) { } @Override - public NicVO findByMacAddress(String macAddress) { + public NicVO findByMacAddress(String macAddress, long networkId) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("macAddress", macAddress); + sc.setParameters("network", networkId); return findOneBy(sc); } + @Override + public List listByMacAddresses(List macAddresses) { + if (CollectionUtils.isEmpty(macAddresses)) { + return Collections.emptyList(); + } + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("macAddress", macAddresses.toArray()); + return listBy(sc); + } + @Override public List findNicsByIpv6GatewayIpv6CidrAndReserver(String ipv6Gateway, String ipv6Cidr, String reserverName) { SearchCriteria sc = AllFieldsSearch.create(); diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicDetailsDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicDetailsDao.java index 2ca901fa3e55..403727ed9c61 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/NicDetailsDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicDetailsDao.java @@ -16,10 +16,14 @@ // under the License. package com.cloud.vm.dao; +import java.util.Set; + import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; import com.cloud.utils.db.GenericDao; import com.cloud.vm.NicDetailVO; public interface NicDetailsDao extends GenericDao, ResourceDetailsDao { + + void removeDetailsForNicIds(String resourceName, Set nicIds); } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicDetailsDaoImpl.java index 2a0b02aae1d9..1011f0588bbc 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/NicDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicDetailsDaoImpl.java @@ -17,17 +17,43 @@ package com.cloud.vm.dao; -import org.springframework.stereotype.Component; +import java.util.Set; +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Component; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; import com.cloud.vm.NicDetailVO; @Component public class NicDetailsDaoImpl extends ResourceDetailsDaoBase implements NicDetailsDao { + private final SearchBuilder ResourceIdNameSearch; + + public NicDetailsDaoImpl() { + super(); + ResourceIdNameSearch = createSearchBuilder(); + ResourceIdNameSearch.and(ApiConstants.NAME, ResourceIdNameSearch.entity().getName(), SearchCriteria.Op.EQ); + ResourceIdNameSearch.and(ApiConstants.RESOURCE_ID, ResourceIdNameSearch.entity().getResourceId(), SearchCriteria.Op.IN); + ResourceIdNameSearch.done(); + } + @Override public void addDetail(long resourceId, String key, String value, boolean display) { super.addDetail(new NicDetailVO(resourceId, key, value, display)); } + + @Override + public void removeDetailsForNicIds(String resourceName, Set nicIds) { + if (CollectionUtils.isEmpty(nicIds)) { + return; + } + SearchCriteria sc = ResourceIdNameSearch.create(); + sc.setParameters(ApiConstants.NAME, resourceName); + sc.setParameters(ApiConstants.RESOURCE_ID, nicIds.toArray()); + remove(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicSecondaryIpVO.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicSecondaryIpVO.java index 4c8208b4be84..baa331d81f67 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/NicSecondaryIpVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicSecondaryIpVO.java @@ -43,7 +43,7 @@ public NicSecondaryIpVO(long nicId, String ipaddr, long vmId, long accountId, lo this.networkId = networkId; } - public NicSecondaryIpVO(long nicId, String ip4Address, String ip6Address, long vmId, long accountId, long domainId, long networkId) { + public NicSecondaryIpVO(long nicId, String ip4Address, String ip6Address, long vmId, long accountId, long domainId, long networkId, String description) { this.nicId = nicId; this.vmId = vmId; this.ip4Address = ip4Address; @@ -51,6 +51,7 @@ public NicSecondaryIpVO(long nicId, String ip4Address, String ip6Address, long v this.accountId = accountId; this.domainId = domainId; this.networkId = networkId; + this.description = description; } protected NicSecondaryIpVO() { @@ -88,6 +89,18 @@ protected NicSecondaryIpVO() { @Column(name = "vmId") long vmId; + @Column(name = "description") + String description; + + @Override + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + @Override public String toString() { return String.format("NicSecondaryIp %s", 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 41bcb3155e54..761053a89f0c 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 @@ -101,7 +101,7 @@ public class UserVmDaoImpl extends GenericDaoBase implements Use ReservationDao reservationDao; private static final String LIST_PODS_HAVING_VMS_FOR_ACCOUNT = - "SELECT pod_id FROM cloud.vm_instance WHERE data_center_id = ? AND account_id = ? AND pod_id IS NOT NULL AND (state = 'Running' OR state = 'Stopped') " + "SELECT pod_id FROM cloud.vm_instance WHERE data_center_id = ? AND account_id = ? AND pod_id IS NOT NULL AND state IN ('Starting', 'Running', 'Stopped') " + "GROUP BY pod_id HAVING count(id) > 0 ORDER BY count(id) DESC"; private static final String VM_DETAILS = "select vm_instance.id, " @@ -782,7 +782,7 @@ public List> countVmsBySize(long dcId, int li result.add(new Ternary(rs.getInt(1), rs.getInt(2), rs.getInt(3))); } } catch (Exception e) { - logger.warn("Error counting vms by size for dcId= " + dcId, e); + logger.warn("Error counting Instances by size for Data Center ID = " + dcId, e); } return result; } 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 f722b4c54e4a..1a5b8cedd9ea 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 @@ -21,6 +21,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import com.cloud.hypervisor.Hypervisor; import com.cloud.utils.Pair; @@ -187,5 +188,15 @@ List searchRemovedByRemoveDate(final Date startDate, final Date en Map getNameIdMapForVmIds(Collection ids); + int getVmCountByOfferingId(Long serviceOfferingId); + + int getVmCountByOfferingNotInDomain(Long serviceOfferingId, List domainIds); + List listByIdsIncludingRemoved(List ids); + + List listDeleteProtectedVmsByAccountId(long accountId); + + List listDeleteProtectedVmsByDomainIds(Set domainIds); + + List listIdsByHostIdForVolumeStats(long hostIds); } 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 2f19d36c37a2..ae1e838649ba 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 @@ -25,11 +25,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import javax.annotation.PostConstruct; import javax.inject.Inject; +import org.apache.cloudstack.api.ApiConstants; import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; @@ -104,6 +106,10 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem protected SearchBuilder LastHostAndStatesSearch; protected SearchBuilder VmsNotInClusterUsingPool; protected SearchBuilder IdsPowerStateSelectSearch; + GenericSearchBuilder CountByOfferingId; + GenericSearchBuilder CountUserVmNotInDomain; + SearchBuilder DeleteProtectedVmSearchByAccount; + SearchBuilder DeleteProtectedVmSearchByDomainIds; @Inject ResourceTagDao tagsDao; @@ -116,17 +122,17 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem protected Attribute _updateTimeAttr; - private static final String ORDER_CLUSTERS_NUMBER_OF_VMS_FOR_ACCOUNT_PART1 = "SELECT host.cluster_id, SUM(IF(vm.state='Running' AND vm.account_id = ?, 1, 0)) " + + private static final String ORDER_CLUSTERS_NUMBER_OF_VMS_FOR_ACCOUNT_PART1 = "SELECT host.cluster_id, SUM(IF(vm.state IN ('Running', 'Starting') AND vm.account_id = ?, 1, 0)) " + "FROM `cloud`.`host` host LEFT JOIN `cloud`.`vm_instance` vm ON host.id = vm.host_id WHERE "; private static final String ORDER_CLUSTERS_NUMBER_OF_VMS_FOR_ACCOUNT_PART2 = " AND host.type = 'Routing' AND host.removed is null GROUP BY host.cluster_id " + "ORDER BY 2 ASC "; - private static final String ORDER_PODS_NUMBER_OF_VMS_FOR_ACCOUNT = "SELECT pod.id, SUM(IF(vm.state='Running' AND vm.account_id = ?, 1, 0)) FROM `cloud`.`" + + private static final String ORDER_PODS_NUMBER_OF_VMS_FOR_ACCOUNT = "SELECT pod.id, SUM(IF(vm.state IN ('Running', 'Starting') AND vm.account_id = ?, 1, 0)) FROM `cloud`.`" + "host_pod_ref` pod LEFT JOIN `cloud`.`vm_instance` vm ON pod.id = vm.pod_id WHERE pod.data_center_id = ? AND pod.removed is null " + " GROUP BY pod.id ORDER BY 2 ASC "; private static final String ORDER_HOSTS_NUMBER_OF_VMS_FOR_ACCOUNT = - "SELECT host.id, SUM(IF(vm.state='Running' AND vm.account_id = ?, 1, 0)) FROM `cloud`.`host` host LEFT JOIN `cloud`.`vm_instance` vm ON host.id = vm.host_id " + + "SELECT host.id, SUM(IF(vm.state IN ('Running', 'Starting') AND vm.account_id = ?, 1, 0)) FROM `cloud`.`host` host LEFT JOIN `cloud`.`vm_instance` vm ON host.id = vm.host_id " + "WHERE host.data_center_id = ? AND host.type = 'Routing' AND host.removed is null "; private static final String ORDER_HOSTS_NUMBER_OF_VMS_FOR_ACCOUNT_PART2 = " GROUP BY host.id ORDER BY 2 ASC "; @@ -352,8 +358,34 @@ protected void init() { IdsPowerStateSelectSearch.entity().getPowerHostId(), IdsPowerStateSelectSearch.entity().getPowerState(), IdsPowerStateSelectSearch.entity().getPowerStateUpdateCount(), - IdsPowerStateSelectSearch.entity().getPowerStateUpdateTime()); + IdsPowerStateSelectSearch.entity().getPowerStateUpdateTime(), + IdsPowerStateSelectSearch.entity().getState()); IdsPowerStateSelectSearch.done(); + + CountByOfferingId = createSearchBuilder(Integer.class); + CountByOfferingId.select(null, Func.COUNT, CountByOfferingId.entity().getId()); + CountByOfferingId.and("serviceOfferingId", CountByOfferingId.entity().getServiceOfferingId(), Op.EQ); + CountByOfferingId.done(); + + CountUserVmNotInDomain = createSearchBuilder(Integer.class); + CountUserVmNotInDomain.select(null, Func.COUNT, CountUserVmNotInDomain.entity().getId()); + CountUserVmNotInDomain.and("serviceOfferingId", CountUserVmNotInDomain.entity().getServiceOfferingId(), Op.EQ); + CountUserVmNotInDomain.and("domainIdsNotIn", CountUserVmNotInDomain.entity().getDomainId(), Op.NIN); + CountUserVmNotInDomain.done(); + + DeleteProtectedVmSearchByAccount = createSearchBuilder(); + DeleteProtectedVmSearchByAccount.selectFields(DeleteProtectedVmSearchByAccount.entity().getUuid()); + DeleteProtectedVmSearchByAccount.and(ApiConstants.ACCOUNT_ID, DeleteProtectedVmSearchByAccount.entity().getAccountId(), Op.EQ); + DeleteProtectedVmSearchByAccount.and(ApiConstants.DELETE_PROTECTION, DeleteProtectedVmSearchByAccount.entity().isDeleteProtection(), Op.EQ); + DeleteProtectedVmSearchByAccount.and(ApiConstants.REMOVED, DeleteProtectedVmSearchByAccount.entity().getRemoved(), Op.NULL); + DeleteProtectedVmSearchByAccount.done(); + + DeleteProtectedVmSearchByDomainIds = createSearchBuilder(); + DeleteProtectedVmSearchByDomainIds.selectFields(DeleteProtectedVmSearchByDomainIds.entity().getUuid()); + DeleteProtectedVmSearchByDomainIds.and(ApiConstants.DOMAIN_IDS, DeleteProtectedVmSearchByDomainIds.entity().getDomainId(), Op.IN); + DeleteProtectedVmSearchByDomainIds.and(ApiConstants.DELETE_PROTECTION, DeleteProtectedVmSearchByDomainIds.entity().isDeleteProtection(), Op.EQ); + DeleteProtectedVmSearchByDomainIds.and(ApiConstants.REMOVED, DeleteProtectedVmSearchByDomainIds.entity().getRemoved(), Op.NULL); + DeleteProtectedVmSearchByDomainIds.done(); } @Override @@ -832,15 +864,18 @@ public HashMap countVgpuVMs(Long dcId, Long podId, Long clusterId) 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 = pstmtLegacy.executeQuery(); while (rs.next()) { result.put(rs.getString(1).concat(rs.getString(2)), rs.getLong(3)); } + + pstmt = txn.prepareAutoCloseStatement(finalQuery.toString()); + for (int i = 0; i < resourceIdList.size(); i++) { + pstmt.setLong(1 + i, resourceIdList.get(i)); + } rs = pstmt.executeQuery(); while (rs.next()) { result.put(rs.getString(1).concat(rs.getString(2)), rs.getLong(3)); @@ -886,7 +921,7 @@ public Long countByZoneAndStateAndHostTag(long dcId, State state, String hostTag return rs.getLong(1); } } catch (Exception e) { - logger.warn(String.format("Error counting vms by host tag for dcId= %s, hostTag= %s", dcId, hostTag), e); + logger.warn("Error counting Instances by host tag for dcId = {}, hostTag = {}", dcId, hostTag, e); } return 0L; } @@ -1071,10 +1106,14 @@ public Map updatePowerState( private boolean isPowerStateInSyncWithInstanceState(final VirtualMachine.PowerState powerState, final long powerHostId, final VMInstanceVO instance) { State instanceState = instance.getState(); + if (instanceState == null) { + logger.warn("VM {} has null instance state during power state sync check, treating as out of sync", instance); + return false; + } if ((powerState == VirtualMachine.PowerState.PowerOff && instanceState == State.Running) || (powerState == VirtualMachine.PowerState.PowerOn && instanceState == State.Stopped)) { HostVO instanceHost = hostDao.findById(instance.getHostId()); - HostVO powerHost = powerHostId == instance.getHostId() ? instanceHost : hostDao.findById(powerHostId); + HostVO powerHost = instance.getHostId() != null && powerHostId == instance.getHostId() ? instanceHost : hostDao.findById(powerHostId); logger.debug("VM: {} on host: {} and power host : {} is in {} state, but power state is {}", instance, instanceHost, powerHost, instanceState, powerState); return false; @@ -1247,6 +1286,29 @@ public Map getNameIdMapForVmIds(Collection ids) { .collect(Collectors.toMap(VMInstanceVO::getInstanceName, VMInstanceVO::getId)); } + @Override + public int getVmCountByOfferingId(Long serviceOfferingId) { + if (serviceOfferingId == null) { + return 0; + } + SearchCriteria sc = CountByOfferingId.create(); + sc.setParameters("serviceOfferingId", serviceOfferingId); + List count = customSearch(sc, null); + return count.get(0); + } + + @Override + public int getVmCountByOfferingNotInDomain(Long serviceOfferingId, List domainIds) { + if (serviceOfferingId == null || CollectionUtils.isEmpty(domainIds)) { + return 0; + } + SearchCriteria sc = CountUserVmNotInDomain.create(); + sc.setParameters("serviceOfferingId", serviceOfferingId); + sc.setParameters("domainIdsNotIn", domainIds.toArray()); + List count = customSearch(sc, null); + return count.get(0); + } + @Override public List listByIdsIncludingRemoved(List ids) { SearchBuilder idsSearch = createSearchBuilder(); @@ -1256,4 +1318,38 @@ public List listByIdsIncludingRemoved(List ids) { sc.setParameters("ids", ids.toArray()); return listIncludingRemovedBy(sc); } + + @Override + public List listDeleteProtectedVmsByAccountId(long accountId) { + SearchCriteria sc = DeleteProtectedVmSearchByAccount.create(); + sc.setParameters(ApiConstants.ACCOUNT_ID, accountId); + sc.setParameters(ApiConstants.DELETE_PROTECTION, true); + Filter filter = new Filter(VMInstanceVO.class, null, false, 0L, 10L); + return listBy(sc, filter); + } + + @Override + public List listDeleteProtectedVmsByDomainIds(Set domainIds) { + SearchCriteria sc = DeleteProtectedVmSearchByDomainIds.create(); + sc.setParameters(ApiConstants.DOMAIN_IDS, domainIds.toArray()); + sc.setParameters(ApiConstants.DELETE_PROTECTION, true); + Filter filter = new Filter(VMInstanceVO.class, null, false, 0L, 10L); + return listBy(sc, filter); + } + + @Override + public List listIdsByHostIdForVolumeStats(long hostId) { + GenericSearchBuilder sb = createSearchBuilder(Long.class); + sb.selectFields(sb.entity().getId()); + sb.and().op("host", sb.entity().getHostId(), SearchCriteria.Op.EQ); + sb.or().op("hostNull", sb.entity().getHostId(), Op.NULL); + sb.and("lastHost", sb.entity().getLastHostId(), SearchCriteria.Op.EQ); + sb.cp(); + sb.cp(); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("host", hostId); + sc.setParameters("lastHost", hostId); + return customSearch(sc, null); + } } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDao.java index ea9ac5afba67..4cbdc516ba0b 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDao.java @@ -22,4 +22,5 @@ import com.cloud.vm.VMInstanceDetailVO; public interface VMInstanceDetailsDao extends GenericDao, ResourceDetailsDao { + int removeDetailsWithPrefix(long vmId, String prefix); } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDaoImpl.java index ca11b005fb2b..4c2fdd6f8d45 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDaoImpl.java @@ -17,10 +17,13 @@ package com.cloud.vm.dao; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; import com.cloud.vm.VMInstanceDetailVO; @Component @@ -31,4 +34,18 @@ public void addDetail(long resourceId, String key, String value, boolean display super.addDetail(new VMInstanceDetailVO(resourceId, key, value, display)); } + @Override + public int removeDetailsWithPrefix(long vmId, String prefix) { + if (StringUtils.isBlank(prefix)) { + return 0; + } + SearchBuilder sb = createSearchBuilder(); + sb.and("vmId", sb.entity().getResourceId(), SearchCriteria.Op.EQ); + sb.and("prefix", sb.entity().getName(), SearchCriteria.Op.LIKE); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("vmId", vmId); + sc.setParameters("prefix", prefix + "%"); + return super.remove(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/VmIsoMapDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/VmIsoMapDao.java new file mode 100644 index 000000000000..a472a3b4dece --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VmIsoMapDao.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.vm.dao; + +import java.util.List; + +import com.cloud.utils.db.GenericDao; +import com.cloud.vm.VmIsoMapVO; + +public interface VmIsoMapDao extends GenericDao { + List listByVmId(long vmId); + + List listByIsoId(long isoId); + + VmIsoMapVO findByVmIdDeviceSeq(long vmId, int deviceSeq); + + VmIsoMapVO findByVmIdIsoId(long vmId, long isoId); + + int removeByVmId(long vmId); +} diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/VmIsoMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/VmIsoMapDaoImpl.java new file mode 100644 index 000000000000..44749eea75f1 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VmIsoMapDaoImpl.java @@ -0,0 +1,92 @@ +// 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.dao; + +import java.util.List; + +import org.springframework.stereotype.Component; + +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.vm.VmIsoMapVO; + +@Component +public class VmIsoMapDaoImpl extends GenericDaoBase implements VmIsoMapDao { + + private SearchBuilder ListByVmId; + private SearchBuilder ListByIsoId; + private SearchBuilder ByVmIdDeviceSeq; + private SearchBuilder ByVmIdIsoId; + + protected VmIsoMapDaoImpl() { + ListByVmId = createSearchBuilder(); + ListByVmId.and("vmId", ListByVmId.entity().getVmId(), SearchCriteria.Op.EQ); + ListByVmId.done(); + + ListByIsoId = createSearchBuilder(); + ListByIsoId.and("isoId", ListByIsoId.entity().getIsoId(), SearchCriteria.Op.EQ); + ListByIsoId.done(); + + ByVmIdDeviceSeq = createSearchBuilder(); + ByVmIdDeviceSeq.and("vmId", ByVmIdDeviceSeq.entity().getVmId(), SearchCriteria.Op.EQ); + ByVmIdDeviceSeq.and("deviceSeq", ByVmIdDeviceSeq.entity().getDeviceSeq(), SearchCriteria.Op.EQ); + ByVmIdDeviceSeq.done(); + + ByVmIdIsoId = createSearchBuilder(); + ByVmIdIsoId.and("vmId", ByVmIdIsoId.entity().getVmId(), SearchCriteria.Op.EQ); + ByVmIdIsoId.and("isoId", ByVmIdIsoId.entity().getIsoId(), SearchCriteria.Op.EQ); + ByVmIdIsoId.done(); + } + + @Override + public List listByVmId(long vmId) { + SearchCriteria sc = ListByVmId.create(); + sc.setParameters("vmId", vmId); + return listBy(sc); + } + + @Override + public List listByIsoId(long isoId) { + SearchCriteria sc = ListByIsoId.create(); + sc.setParameters("isoId", isoId); + return listBy(sc); + } + + @Override + public VmIsoMapVO findByVmIdDeviceSeq(long vmId, int deviceSeq) { + SearchCriteria sc = ByVmIdDeviceSeq.create(); + sc.setParameters("vmId", vmId); + sc.setParameters("deviceSeq", deviceSeq); + return findOneBy(sc); + } + + @Override + public VmIsoMapVO findByVmIdIsoId(long vmId, long isoId) { + SearchCriteria sc = ByVmIdIsoId.create(); + sc.setParameters("vmId", vmId); + sc.setParameters("isoId", isoId); + return findOneBy(sc); + } + + @Override + public int removeByVmId(long vmId) { + SearchCriteria sc = ListByVmId.create(); + sc.setParameters("vmId", vmId); + return remove(sc); + } +} 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 4045af58d4bc..39ae3d4f2fdd 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 @@ -42,6 +42,8 @@ public interface VMSnapshotDao extends GenericDao, StateDao< VMSnapshotVO findByName(Long vmId, String name); List listByAccountId(Long accountId); + List searchByVms(List vmIds); + List searchRemovedByVms(List vmIds, Long batchSize); } 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 83411b3cf8f7..7f74d2338e43 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 @@ -203,7 +203,7 @@ public boolean updateState(State currentState, Event event, State nextState, VMS .append("; updatedTime=") .append(oldUpdatedTime); } else { - logger.debug("Unable to update VM snapshot: {}, as there is no such snapshot exists in the database anymore", vo); + logger.debug("Unable to update Instance Snapshot: {}, as there is no such Snapshot exists in the database anymore", vo); } } return rows > 0; diff --git a/engine/schema/src/main/java/org/apache/cloudstack/acl/ApiKeyPairPermissionVO.java b/engine/schema/src/main/java/org/apache/cloudstack/acl/ApiKeyPairPermissionVO.java new file mode 100644 index 000000000000..7972fe6bc624 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/acl/ApiKeyPairPermissionVO.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.acl; + +import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +@Entity +@Table(name = "api_keypair_permissions") +public class ApiKeyPairPermissionVO extends RolePermissionBaseVO implements ApiKeyPairPermission { + @Column(name = "api_keypair_id") + private long apiKeyPairId; + + @Column(name = "sort_order") + private long sortOrder = 0; + + public ApiKeyPairPermissionVO(long apiKeyPairId, String rule, Permission permission, String description) { + super(rule, permission, description); + this.apiKeyPairId = apiKeyPairId; + } + + public ApiKeyPairPermissionVO(String rule, Permission permission, String description) { + super(rule, permission, description); + } + + public ApiKeyPairPermissionVO() { + } + + public long getApiKeyPairId() { + return this.apiKeyPairId; + } + + public void setApiKeyPairId(long keyPairId) { + this.apiKeyPairId = keyPairId; + } + + public void setSortOrder(long sortOrder) { + this.sortOrder = sortOrder; + } + + public long getSortOrder() { + return sortOrder; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/acl/ApiKeyPairVO.java b/engine/schema/src/main/java/org/apache/cloudstack/acl/ApiKeyPairVO.java new file mode 100644 index 000000000000..7a6bd7a3b140 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/acl/ApiKeyPairVO.java @@ -0,0 +1,244 @@ +// 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.acl; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.utils.db.Encrypt; +import java.time.Instant; +import org.apache.cloudstack.acl.apikeypair.ApiKeyPair; + +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.Objects; +import java.util.UUID; +import org.joda.time.DateTime; + +@Entity +@Table(name = "api_keypair") +public class ApiKeyPairVO implements ApiKeyPair { + @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 = "user_id", nullable = false) + private Long userId; + + @Column(name = "name", nullable = false) + private String name; + + @Column(name = "domain_id", nullable = false) + private Long domainId; + + @Column(name = "account_id", nullable = false) + private Long accountId; + + @Column(name = "start_date") + @Temporal(value = TemporalType.TIMESTAMP) + private Date startDate; + + @Column(name = "end_date") + @Temporal(value = TemporalType.TIMESTAMP) + private Date endDate; + + @Column(name = "created", nullable = false) + @Temporal(value = TemporalType.TIMESTAMP) + private Date created = Date.from(Instant.now()); + + @Column(name = "description", length = 1024) + private String description = ""; + + @Column(name = "api_key", nullable = false) + private String apiKey; + + @Encrypt + @Column(name = "secret_key", nullable = false) + private String secretKey; + + @Column(name = "removed") + @Temporal(value = TemporalType.TIMESTAMP) + private Date removed; + + public ApiKeyPairVO() { + } + + public ApiKeyPairVO(Long id) { + this.id = id; + } + + public ApiKeyPairVO(Long userId, String description, Date startDate, Date endDate, + String apiKey, String secretKey) { + this.userId = userId; + this.description = description; + this.startDate = startDate; + this.endDate = endDate; + this.apiKey = apiKey; + this.secretKey = secretKey; + } + + public ApiKeyPairVO(String name, Long userId, String description, Date startDate, Date endDate, Account account) { + this.name = Objects.requireNonNullElseGet(name, () -> userId + " - API key pair"); + this.userId = userId; + this.description = description; + this.startDate = startDate; + this.endDate = endDate; + this.domainId = account.getDomainId(); + this.accountId = account.getAccountId(); + } + + public ApiKeyPairVO(Long id, Long userId) { + this.id = id; + this.userId = userId; + } + + public void validateDate() { + Date now = DateTime.now().toDate(); + Date keypairStart = this.getStartDate(); + Date keypairExpiration = this.getEndDate(); + if (keypairStart != null && now.compareTo(keypairStart) <= 0) { + throw new InvalidParameterValueException(String.format("API key pair is not valid yet, start date: %s", keypairStart)); + } + if (keypairExpiration != null && now.compareTo(keypairExpiration) >= 0) { + throw new InvalidParameterValueException(String.format("API key pair is expired, expiration date: %s", keypairExpiration)); + } + } + + public boolean hasEndDatePassed() { + Date now = DateTime.now().toDate(); + Date keypairExpiration = this.getEndDate(); + return keypairExpiration != null && now.compareTo(keypairExpiration) >= 0; + } + + public long getId() { + return id; + } + + public String getUuid() { + return uuid; + } + + public Long getUserId() { + return userId; + } + + public Date getStartDate() { + return startDate; + } + + public Date getEndDate() { + return endDate; + } + + public Date getCreated() { + return created; + } + + public String getDescription() { + return description; + } + + public String getApiKey() { + return apiKey; + } + + public String getSecretKey() { + return secretKey; + } + + public Class getEntityType() { + return ApiKeyPair.class; + } + + public String getName() { + return name; + } + + public Date getRemoved() { + return removed; + } + + @Override + public long getDomainId() { + return this.domainId; + } + + @Override + public long getAccountId() { + return this.accountId; + } + + public void setId(Long id) { this.id = id; } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public void setStartDate(Date startDate) { + this.startDate = startDate; + } + + public void setEndDate(Date endDate) { + this.endDate = endDate; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + public void setSecretKey(String secretKey) { + this.secretKey = secretKey; + } + + public void setName(String name) { + this.name = name; + } + + public void setDomainId(Long domainId) { + this.domainId = domainId; + } + + public void setAccountId(Long accountId) { + this.accountId = accountId; + } + + public void setCreated(Date created) { + this.created = created; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/acl/RolePermissionBaseVO.java b/engine/schema/src/main/java/org/apache/cloudstack/acl/RolePermissionBaseVO.java index f3347ab66305..588fa0299649 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/acl/RolePermissionBaseVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/acl/RolePermissionBaseVO.java @@ -50,6 +50,12 @@ public class RolePermissionBaseVO implements RolePermissionEntity { public RolePermissionBaseVO() { this.uuid = UUID.randomUUID().toString(); } + public RolePermissionBaseVO(final String rule, final Permission permission) { + this(); + this.rule = rule; + this.permission = permission; + } + public RolePermissionBaseVO(final String rule, final Permission permission, final String description) { this(); this.rule = rule; diff --git a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/ApiKeyPairDao.java b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/ApiKeyPairDao.java new file mode 100644 index 000000000000..006c2afbc96e --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/ApiKeyPairDao.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.acl.dao; + +import com.cloud.utils.Pair; +import java.util.List; +import org.apache.cloudstack.acl.ApiKeyPairVO; +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.api.command.admin.user.ListUserKeysCmd; + +public interface ApiKeyPairDao extends GenericDao { + ApiKeyPairVO findBySecretKey(String secretKey); + + ApiKeyPairVO findByApiKey(String apiKey); + + ApiKeyPairVO findByUuid(String uuid); + + Pair, Integer> listApiKeysByUserOrApiKeyId(Long userId, Long apiKeyId); + + ApiKeyPairVO getLastApiKeyCreatedByUser(Long userId); + + Pair, Integer> listByUserIdsPaginated(List userIds, ListUserKeysCmd cmd); + +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/ApiKeyPairDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/ApiKeyPairDaoImpl.java new file mode 100644 index 000000000000..a4895efed9e0 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/ApiKeyPairDaoImpl.java @@ -0,0 +1,92 @@ +// 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.acl.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 java.util.List; +import org.apache.cloudstack.acl.ApiKeyPairVO; +import org.apache.cloudstack.api.command.admin.user.ListUserKeysCmd; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Component; + +@Component +public class ApiKeyPairDaoImpl extends GenericDaoBase implements ApiKeyPairDao { + private static final String ID = "id"; + private static final String USER_ID = "userId"; + private static final String API_KEY = "apiKey"; + private static final String SECRET_KEY = "secretKey"; + + private final SearchBuilder keyPairSearch; + + ApiKeyPairDaoImpl() { + super(); + + keyPairSearch = createSearchBuilder(); + keyPairSearch.and(API_KEY, keyPairSearch.entity().getApiKey(), SearchCriteria.Op.EQ); + keyPairSearch.and(SECRET_KEY, keyPairSearch.entity().getSecretKey(), SearchCriteria.Op.EQ); + keyPairSearch.and(ID, keyPairSearch.entity().getId(), SearchCriteria.Op.EQ); + keyPairSearch.and(USER_ID, keyPairSearch.entity().getUserId(), SearchCriteria.Op.IN); + keyPairSearch.done(); + } + + @Override + public ApiKeyPairVO findByApiKey(String apiKey) { + SearchCriteria sc = keyPairSearch.create(); + sc.setParameters(API_KEY, apiKey); + return findOneBy(sc); + } + + public ApiKeyPairVO findBySecretKey(String secretKey) { + SearchCriteria sc = keyPairSearch.create(); + sc.setParameters(SECRET_KEY, secretKey); + return findOneBy(sc); + } + + public Pair, Integer> listApiKeysByUserOrApiKeyId(Long userId, Long apiKeyId) { + SearchCriteria sc = keyPairSearch.create(); + sc.setParametersIfNotNull(USER_ID, userId); + sc.setParametersIfNotNull(ID, apiKeyId); + final Filter searchFilter = new Filter(100); + return searchAndCount(sc, searchFilter); + } + + public ApiKeyPairVO getLastApiKeyCreatedByUser(Long userId) { + final SearchCriteria sc = keyPairSearch.create(); + sc.setParametersIfNotNull(USER_ID, userId); + final Filter searchBySorted = new Filter(ApiKeyPairVO.class, ID, false, null, null); + return findOneBy(sc, searchBySorted); + } + + public Pair, Integer> listByUserIdsPaginated(List userIds, ListUserKeysCmd cmd) { + Long pageSizeVal = cmd.getPageSizeVal(); + Long startIndex = cmd.getStartIndex(); + Filter searchFilter = new Filter(ApiKeyPairVO.class, ID, true, startIndex, pageSizeVal); + + final SearchCriteria sc = keyPairSearch.create(); + sc.setParameters(USER_ID, (Object[]) userIds.toArray(new Long[0])); + + Pair, Integer> apiKeyPairVOList = searchAndCount(sc, searchFilter); + if (CollectionUtils.isEmpty(apiKeyPairVOList.first())) { + return new Pair<>(List.of(), 0); + } + return apiKeyPairVOList; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/ApiKeyPairPermissionsDao.java b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/ApiKeyPairPermissionsDao.java new file mode 100644 index 000000000000..cbca2fd72747 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/ApiKeyPairPermissionsDao.java @@ -0,0 +1,28 @@ +// 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.acl.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.acl.ApiKeyPairPermissionVO; + +import java.util.List; + +public interface ApiKeyPairPermissionsDao extends GenericDao { + List findAllByApiKeyPairId(Long apiKeyPairId); + + List findAllByKeyPairIdSorted(Long apiKeyPairId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/ApiKeyPairPermissionsDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/ApiKeyPairPermissionsDaoImpl.java new file mode 100644 index 000000000000..4510e0ae2896 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/ApiKeyPairPermissionsDaoImpl.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 org.apache.cloudstack.acl.dao; + +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 java.util.Collections; +import java.util.Objects; +import org.apache.commons.collections.CollectionUtils; +import org.apache.cloudstack.acl.ApiKeyPairPermissionVO; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +public class ApiKeyPairPermissionsDaoImpl extends GenericDaoBase implements ApiKeyPairPermissionsDao { + private static final String API_KEY_PAIR_ID = "apiKeyPairId"; + private static final String SORT_ORDER = "sortOrder"; + + private final SearchBuilder permissionByApiKeyPairIdSearch; + + public ApiKeyPairPermissionsDaoImpl() { + super(); + + permissionByApiKeyPairIdSearch = createSearchBuilder(); + permissionByApiKeyPairIdSearch.and(API_KEY_PAIR_ID, permissionByApiKeyPairIdSearch.entity().getApiKeyPairId(), SearchCriteria.Op.EQ); + permissionByApiKeyPairIdSearch.done(); + } + + public List findAllByApiKeyPairId(Long apiKeyPairId) { + SearchCriteria sc = permissionByApiKeyPairIdSearch.create(); + sc.setParameters(API_KEY_PAIR_ID, String.valueOf(apiKeyPairId)); + return listBy(sc); + } + + @Override + public ApiKeyPairPermissionVO persist(final ApiKeyPairPermissionVO item) { + item.setSortOrder(0); + final List permissionsList = findAllByKeyPairIdSorted(item.getApiKeyPairId()); + if (!CollectionUtils.isEmpty(permissionsList)) { + ApiKeyPairPermissionVO lastPermission = permissionsList.get(permissionsList.size() - 1); + item.setSortOrder(lastPermission.getSortOrder() + 1); + } + return super.persist(item); + } + + @Override + public List findAllByKeyPairIdSorted(Long apiKeyPairId) { + final SearchCriteria sc = permissionByApiKeyPairIdSearch.create(); + sc.setParameters(API_KEY_PAIR_ID, apiKeyPairId); + final Filter searchBySorted = new Filter(ApiKeyPairPermissionVO.class, SORT_ORDER, true, null, null); + final List apiKeyPairPermissionList = listBy(sc, searchBySorted); + return Objects.requireNonNullElse(apiKeyPairPermissionList, Collections.emptyList()); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupOfferingDetailsVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupOfferingDetailsVO.java new file mode 100644 index 000000000000..6bdf7602a9d4 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupOfferingDetailsVO.java @@ -0,0 +1,86 @@ +// 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_offering_details") +public class BackupOfferingDetailsVO implements ResourceDetail { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "backup_offering_id") + private long resourceId; + + @Column(name = "name") + private String name; + + @Column(name = "value") + private String value; + + @Column(name = "display") + private boolean display = true; + + protected BackupOfferingDetailsVO() { + } + + public BackupOfferingDetailsVO(long backupOfferingId, String name, String value, boolean display) { + this.resourceId = backupOfferingId; + this.name = name; + this.value = value; + this.display = display; + } + + @Override + public long getResourceId() { + return resourceId; + } + + public void setResourceId(long backupOfferingId) { + this.resourceId = backupOfferingId; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getValue() { + return value; + } + + @Override + public long getId() { + return id; + } + + @Override + public boolean isDisplay() { + return display; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupOfferingVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupOfferingVO.java index d30385af575d..9156f8f5ff84 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupOfferingVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupOfferingVO.java @@ -17,6 +17,8 @@ package org.apache.cloudstack.backup; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + import java.util.Date; import java.util.UUID; @@ -81,6 +83,11 @@ public BackupOfferingVO(final long zoneId, final String externalId, final String this.created = new Date(); } + public BackupOfferingVO(final long zoneId, final String provider, final String name, final String description, final boolean userDrivenBackupAllowed) { + this(zoneId, null, provider, name, description, userDrivenBackupAllowed); + this.externalId = this.uuid; + } + public String getUuid() { return uuid; } @@ -131,4 +138,9 @@ public void setDescription(String description) { public Date getCreated() { return created; } + + @Override + public String toString() { + return String.format("Backup offering %s.", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "name", "uuid")); + } } 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 98efa94ceca2..1764496a6c0b 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 @@ -67,6 +67,9 @@ public class BackupRepositoryVO implements BackupRepository { @Column(name = "capacity_bytes", nullable = true) private Long capacityBytes; + @Column(name = "cross_zone_instance_creation") + private Boolean crossZoneInstanceCreation; + @Column(name = "created") @Temporal(value = TemporalType.TIMESTAMP) private Date created; @@ -79,7 +82,7 @@ public BackupRepositoryVO() { this.uuid = UUID.randomUUID().toString(); } - public BackupRepositoryVO(final long zoneId, final String provider, final String name, final String type, final String address, final String mountOptions, final Long capacityBytes) { + public BackupRepositoryVO(final long zoneId, final String provider, final String name, final String type, final String address, final String mountOptions, final Long capacityBytes, final Boolean crossZoneInstanceCreation) { this(); this.zoneId = zoneId; this.provider = provider; @@ -88,6 +91,7 @@ public BackupRepositoryVO(final long zoneId, final String provider, final String this.address = address; this.mountOptions = mountOptions; this.capacityBytes = capacityBytes; + this.crossZoneInstanceCreation = crossZoneInstanceCreation; this.created = new Date(); } @@ -139,6 +143,11 @@ public String getMountOptions() { return mountOptions; } + @Override + public void setMountOptions(String mountOptions) { + this.mountOptions = mountOptions; + } + @Override public Long getUsedBytes() { return usedBytes; @@ -154,6 +163,16 @@ public Long getCapacityBytes() { return capacityBytes; } + @Override + public Boolean crossZoneInstanceCreationEnabled() { + return crossZoneInstanceCreation; + } + + @Override + public void setCrossZoneInstanceCreation(Boolean crossZoneInstanceCreation) { + this.crossZoneInstanceCreation = crossZoneInstanceCreation; + } + @Override public void setCapacityBytes(Long capacityBytes) { this.capacityBytes = capacityBytes; 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 37e8105e3d51..aa02bb077163 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 @@ -68,10 +68,20 @@ public class BackupScheduleVO implements BackupSchedule { @Column(name = "quiescevm") Boolean quiesceVM = false; + @Column(name = "account_id") + Long accountId; + + @Column(name = "domain_id") + Long domainId; + + @Column(name = "isolated") + private boolean isolated; + public BackupScheduleVO() { } - public BackupScheduleVO(Long vmId, DateUtil.IntervalType scheduleType, String schedule, String timezone, Date scheduledTimestamp, int maxBackups, Boolean quiesceVM) { + public BackupScheduleVO(Long vmId, DateUtil.IntervalType scheduleType, String schedule, String timezone, Date scheduledTimestamp, int maxBackups, Boolean quiesceVM, + Long accountId, Long domainId, boolean isolated) { this.vmId = vmId; this.scheduleType = (short) scheduleType.ordinal(); this.schedule = schedule; @@ -79,6 +89,9 @@ public BackupScheduleVO(Long vmId, DateUtil.IntervalType scheduleType, String sc this.scheduledTimestamp = scheduledTimestamp; this.maxBackups = maxBackups; this.quiesceVM = quiesceVM; + this.accountId = accountId; + this.domainId = domainId; + this.isolated = isolated; } @Override @@ -161,4 +174,41 @@ public void setQuiesceVM(Boolean quiesceVM) { public Boolean getQuiesceVM() { return quiesceVM; } + + @Override + public Class getEntityType() { + return BackupSchedule.class; + } + + @Override + public String getName() { + return null; + } + + @Override + public long getDomainId() { + return domainId; + } + + @Override + public long getAccountId() { + return accountId; + } + + public void setAccountId(Long accountId) { + this.accountId = accountId; + } + + public void setDomainId(Long domainId) { + this.domainId = domainId; + } + + @Override + public boolean isIsolated() { + return isolated; + } + + public void setIsolated(boolean isolated) { + this.isolated = isolated; + } } 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 0f8a10fb7be6..b4cad92d8770 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,7 +19,6 @@ import com.cloud.utils.db.GenericDao; import com.google.gson.Gson; - import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.commons.lang3.StringUtils; @@ -66,7 +65,7 @@ public class BackupVO implements Backup { private String externalId; @Column(name = "type") - private String backupType; + private String type; @Column(name = "date") @Temporal(value = TemporalType.DATE) @@ -81,6 +80,9 @@ public class BackupVO implements Backup { @Column(name = "protected_size") private Long protectedSize; + @Column(name = "uncompressed_size") + private Long uncompressedSize; + @Enumerated(value = EnumType.STRING) @Column(name = "status") private Backup.Status status; @@ -103,6 +105,24 @@ public class BackupVO implements Backup { @Column(name = "backup_schedule_id") private Long backupScheduleId; + @Column(name = "compression_status") + private CompressionStatus compressionStatus; + + @Column(name = "validation_status") + private ValidationStatus validationStatus; + + @Column(name = "from_checkpoint_id") + private String fromCheckpointId; + + @Column(name = "to_checkpoint_id") + private String toCheckpointId; + + @Column(name = "checkpoint_create_time") + private Long checkpointCreateTime; + + @Column(name = "host_id") + private Long hostId; + @Transient Map details; @@ -110,10 +130,27 @@ public BackupVO() { this.uuid = UUID.randomUUID().toString(); } + public BackupVO(String name, long vmId, long backupOfferingId, long accountId, long domainId, long zoneId, long virtualSize, + Status status, Long backupScheduleId, CompressionStatus compressionStatus, ValidationStatus validationStatus) { + this.name = name; + this.vmId = vmId; + this.backupOfferingId = backupOfferingId; + this.accountId = accountId; + this.domainId = domainId; + this.zoneId = zoneId; + this.protectedSize = virtualSize; + this.status = status; + this.setType("FULL"); + this.uuid = UUID.randomUUID().toString(); + this.backupScheduleId = backupScheduleId; + this.compressionStatus = compressionStatus; + this.validationStatus = validationStatus; + } + @Override public String toString() { return String.format("Backup %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( - this, "id", "uuid", "vmId", "backupType", "externalId")); + this, "id", "uuid", "vmId", "type", "externalId")); } @Override @@ -144,12 +181,13 @@ public void setExternalId(String externalId) { this.externalId = externalId; } + @Override public String getType() { - return backupType; + return type; } public void setType(String type) { - this.backupType = type; + this.type = type; } @Override @@ -288,4 +326,66 @@ public Long getBackupScheduleId() { public void setBackupScheduleId(Long backupScheduleId) { this.backupScheduleId = backupScheduleId; } + + @Override + public CompressionStatus getCompressionStatus() { + return compressionStatus; + } + + public void setCompressionStatus(CompressionStatus compressionStatus) { + this.compressionStatus = compressionStatus; + } + + @Override + public ValidationStatus getValidationStatus() { + return validationStatus; + } + + public void setValidationStatus(ValidationStatus validationStatus) { + this.validationStatus = validationStatus; + } + + public Long getUncompressedSize() { + return uncompressedSize; + } + + public void setUncompressedSize(Long uncompressedSize) { + this.uncompressedSize = uncompressedSize; + } + + @Override + public String getFromCheckpointId() { + return fromCheckpointId; + } + + public void setFromCheckpointId(String fromCheckpointId) { + this.fromCheckpointId = fromCheckpointId; + } + + @Override + public String getToCheckpointId() { + return toCheckpointId; + } + + public void setToCheckpointId(String toCheckpointId) { + this.toCheckpointId = toCheckpointId; + } + + @Override + public Long getCheckpointCreateTime() { + return checkpointCreateTime; + } + + public void setCheckpointCreateTime(Long checkpointCreateTime) { + this.checkpointCreateTime = checkpointCreateTime; + } + + @Override + public Long getHostId() { + return hostId; + } + + public void setHostId(Long hostId) { + this.hostId = hostId; + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/ImageTransferVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/ImageTransferVO.java new file mode 100644 index 000000000000..ec9b927b63e4 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/ImageTransferVO.java @@ -0,0 +1,242 @@ +//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.backup; + +import java.util.Date; + +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 javax.persistence.Temporal; +import javax.persistence.TemporalType; + +@Entity +@Table(name = "image_transfer") +public class ImageTransferVO implements ImageTransfer { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "backup_id") + private Long backupId; + + @Column(name = "volume_id") + private long volumeId; + + @Column(name = "host_id") + private long hostId; + + @Column(name = "socket") + private String socket; + + @Column(name = "file") + private String file; + + @Column(name = "transfer_url") + private String transferUrl; + + @Enumerated(value = EnumType.STRING) + @Column(name = "phase") + private Phase phase; + + @Enumerated(value = EnumType.STRING) + @Column(name = "direction") + private Direction direction; + + @Enumerated(value = EnumType.STRING) + @Column(name = "backend") + private Backend backend; + + @Column(name = "signed_ticket_id") + private String signedTicketId; + + @Column(name = "account_id") + Long accountId; + + @Column(name = "domain_id") + Long domainId; + + @Column(name = "data_center_id") + Long dataCenterId; + + @Column(name = "created") + @Temporal(value = TemporalType.TIMESTAMP) + private Date created; + + @Column(name = "updated") + @Temporal(value = TemporalType.TIMESTAMP) + private Date updated; + + @Column(name = "removed") + @Temporal(value = TemporalType.TIMESTAMP) + private Date removed; + + public ImageTransferVO() { + } + + private ImageTransferVO(String uuid, long volumeId, long hostId, Phase phase, Direction direction, Long accountId, Long domainId, Long dataCenterId) { + this.uuid = uuid; + this.volumeId = volumeId; + this.hostId = hostId; + this.phase = phase; + this.direction = direction; + this.accountId = accountId; + this.domainId = domainId; + this.dataCenterId = dataCenterId; + this.created = new Date(); + } + + public ImageTransferVO(String uuid, Long backupId, long volumeId, long hostId, String socket, Phase phase, Direction direction, Long accountId, Long domainId, Long dataCenterId) { + this(uuid, volumeId, hostId, phase, direction, accountId, domainId, dataCenterId); + this.backupId = backupId; + this.socket = socket; + this.backend = Backend.nbd; + } + + public ImageTransferVO(String uuid, long volumeId, long hostId, String file, Phase phase, Direction direction, Long accountId, Long domainId, Long dataCenterId) { + this(uuid, volumeId, hostId, phase, direction, accountId, domainId, dataCenterId); + this.file = file; + this.backend = Backend.file; + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public Long getBackupId() { + return backupId; + } + + public void setBackupId(long backupId) { + this.backupId = backupId; + } + + @Override + public long getVolumeId() { + return volumeId; + } + + public void setVolumeId(long volumeId) { + this.volumeId = volumeId; + } + + @Override + public long getHostId() { + return hostId; + } + + public void setHostId(long hostId) { + this.hostId = hostId; + } + + public void setSocket(String socket) { + this.socket = socket; + } + + @Override + public String getTransferUrl() { + return transferUrl; + } + + public void setTransferUrl(String transferUrl) { + this.transferUrl = transferUrl; + } + + @Override + public Phase getPhase() { + return phase; + } + + public void setPhase(Phase phase) { + this.phase = phase; + this.updated = new Date(); + } + + @Override + public Direction getDirection() { + return direction; + } + + public void setDirection(Direction direction) { + this.direction = direction; + } + + @Override + public Backend getBackend() { + return backend; + } + + @Override + public String getSignedTicketId() { + return signedTicketId; + } + + public void setSignedTicketId(String signedTicketId) { + this.signedTicketId = signedTicketId; + } + + @Override + public Class getEntityType() { + return ImageTransfer.class; + } + + @Override + public String getName() { + return null; + } + + @Override + public long getDomainId() { + return domainId; + } + + @Override + public long getAccountId() { + return accountId; + } + + @Override + public long getDataCenterId() { + return dataCenterId; + } + + public Date getCreated() { + return created; + } + + public Date getUpdated() { + return updated; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupDataStoreVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupDataStoreVO.java new file mode 100644 index 000000000000..5266a4129905 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupDataStoreVO.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 +//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 org.apache.cloudstack.api.InternalIdentity; +import org.apache.commons.lang3.builder.ReflectionToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +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 = "internal_backup_store_ref") +public class InternalBackupDataStoreVO implements InternalIdentity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "backup_id") + private long backupId; + + @Column(name = "volume_id") + private long volumeId; + + @Column (name = "device_id") + private long deviceId; + + @Column(name = "path") + private String backupPath; + + public InternalBackupDataStoreVO() { + } + + public InternalBackupDataStoreVO(long backupId, long volumeId, long deviceId, String backupPath) { + this.backupId = backupId; + this.volumeId = volumeId; + this.deviceId = deviceId; + this.backupPath = backupPath; + } + + @Override + public long getId() { + return id; + } + + public long getBackupId() { + return backupId; + } + + public long getVolumeId() { + return volumeId; + } + + public long getDeviceId() { + return deviceId; + } + + public String getBackupPath() { + return backupPath; + } + + public void setVolumeId(long volumeId) { + this.volumeId = volumeId; + } + + public void setBackupPath(String backupPath) { + this.backupPath = backupPath; + } + + @Override + public String toString() { + return ReflectionToStringBuilder.toString(this, ToStringStyle.JSON_STYLE); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupJoinVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupJoinVO.java new file mode 100644 index 000000000000..acc54ccd41fa --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupJoinVO.java @@ -0,0 +1,190 @@ +// 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.google.gson.Gson; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.builder.ReflectionToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import org.apache.commons.lang3.StringUtils; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +@Entity +@Table(name = "internal_backup_view") +public class InternalBackupJoinVO { + + @Id + @Column(name="id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "vm_id") + private long vmId; + + @Column(name = "backed_volumes", length = 65535) + private String backedUpVolumes; + + @Column(name = "backup_offering_id") + private long backupOfferingId; + + @Column(name = "image_store_id") + private long imageStoreId; + + @Column(name = "parent_id") + private long parentId; + + @Column(name = "type") + private String type; + + @Column(name = "date") + @Temporal(value = TemporalType.DATE) + private Date date; + + @Enumerated(value = EnumType.STRING) + @Column(name = "status") + private Backup.Status status; + + @Enumerated(value = EnumType.STRING) + @Column(name = "compression_status") + private Backup.CompressionStatus compressionStatus; + + @Column(name = "end_of_chain") + private Boolean endOfChain; + + @Column(name = "current") + private Boolean current; + + @Column(name = "image_store_path") + private String imageStorePath; + + @Column(name = "zone_id") + private long zoneId; + + @Column(name = "size") + private long size; + + @Column(name = "protected_size") + private long protectedSize; + + @Column(name = "volume_id") + private long volumeId; + + @Column(name = "isolated") + private Boolean isolated; + + public InternalBackupJoinVO() { + } + + public long getId() { + return id; + } + + public String getUuid() { + return uuid; + } + + public long getVmId() { + return vmId; + } + + public List getBackedUpVolumes() { + if (StringUtils.isEmpty(this.backedUpVolumes)) { + return Collections.emptyList(); + } + return Arrays.asList(new Gson().fromJson(this.backedUpVolumes, Backup.VolumeInfo[].class)); + } + + public long getBackupOfferingId() { + return backupOfferingId; + } + + public long getImageStoreId() { + return imageStoreId; + } + + public long getParentId() { + return parentId; + } + + public String getType() { + return type; + } + + public Date getDate() { + return date; + } + + public Backup.Status getStatus() { + return status; + } + + public Boolean getEndOfChain() { + return BooleanUtils.isTrue(endOfChain); + } + + public Boolean getCurrent() { + return BooleanUtils.isTrue(current); + } + + public String getImageStorePath() { + return imageStorePath; + } + + public long getZoneId() { + return zoneId; + } + + public long getSize() { + return size; + } + + public long getProtectedSize() { + return protectedSize; + } + + public long getVolumeId() { + return volumeId; + } + + public Boolean getIsolated() { + return BooleanUtils.isTrue(isolated); + } + + public Backup.CompressionStatus getCompressionStatus() { + return compressionStatus; + } + + @Override + public String toString() { + return ReflectionToStringBuilder.toString(this, ToStringStyle.JSON_STYLE); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupServiceJobType.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupServiceJobType.java new file mode 100644 index 000000000000..287b64c2e6fc --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupServiceJobType.java @@ -0,0 +1,21 @@ +// 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; + +public enum InternalBackupServiceJobType { + StartCompression, FinalizeCompression, BackupValidation +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupServiceJobVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupServiceJobVO.java new file mode 100644 index 000000000000..87b4a53cb615 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupServiceJobVO.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.backup; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.api.InternalIdentity; +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; + +@Entity +@Table(name = "internal_backup_service_job") +public class InternalBackupServiceJobVO implements InternalIdentity, Comparable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "backup_id") + private long backupId; + + @Column(name = "instance_id") + private long instanceId; + + @Column(name = "account_id") + private long accountId; + + @Column(name = "host_id") + private Long hostId; + + @Column(name = "zone_id") + private long zoneId; + + @Column(name = "attempts") + private int attempts; + + @Column (name = "type") + private InternalBackupServiceJobType type; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + @Column(name = "scheduled_start_time") + @Temporal(value = TemporalType.TIMESTAMP) + private Date scheduledStartTime; + + @Column(name = "start_time") + @Temporal(value = TemporalType.TIMESTAMP) + private Date startTime; + + @Column(name = GenericDao.REMOVED_COLUMN) + @Temporal(value = TemporalType.TIMESTAMP) + private Date removed; + + public InternalBackupServiceJobVO() { + } + + public InternalBackupServiceJobVO(long backupId, long zoneId, long instanceId, long accountId, InternalBackupServiceJobType type) { + this.created = new Date(); + this.backupId = backupId; + this.zoneId = zoneId; + this.instanceId = instanceId; + this.accountId = accountId; + this.type = type; + this.scheduledStartTime = this.created; + } + + public InternalBackupServiceJobVO(long backupId, long zoneId, long instanceId, long accountId, InternalBackupServiceJobType type, Date scheduledStartTime) { + this(backupId, zoneId, instanceId, accountId, type); + this.scheduledStartTime = scheduledStartTime; + } + + @Override + public long getId() { + return id; + } + + public long getBackupId() { + return backupId; + } + + public long getInstanceId() { + return instanceId; + } + + public long getAccountId() { + return accountId; + } + + public Long getHostId() { + return hostId; + } + + public void setHostId(Long hostId) { + this.hostId = hostId; + } + + public long getZoneId() { + return zoneId; + } + + public void setZoneId(Long zoneId) { + this.zoneId = zoneId; + } + + public int getAttempts() { + return attempts; + } + + public void setAttempts(int attempts) { + this.attempts = attempts; + } + + public InternalBackupServiceJobType getType() { + return type; + } + + public Date getCreated() { + return created; + } + + public Date getScheduledStartTime() { + return scheduledStartTime; + } + + public void setScheduledStartTime(Date scheduledStartTime) { + this.scheduledStartTime = scheduledStartTime; + } + + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public Date getRemoved() { + return removed; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } + + @Override + public int compareTo(InternalBackupServiceJobVO that) { + return this.created.compareTo(that.created); + } + + @Override + public String toString() { + return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "backupId", "zoneId", "hostId", "created", "scheduledStartTime", "startTime", "attempts", + "type"); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupStoragePoolVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupStoragePoolVO.java new file mode 100644 index 000000000000..f6d4cd10ffb7 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupStoragePoolVO.java @@ -0,0 +1,101 @@ +//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.backup; + +import org.apache.cloudstack.api.InternalIdentity; +import org.apache.commons.lang3.builder.ReflectionToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +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 = "internal_backup_pool_ref") +public class InternalBackupStoragePoolVO implements InternalIdentity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "backup_id") + private long backupId; + + @Column(name = "storage_pool_id") + private long storagePoolId; + + @Column(name = "volume_id") + private long volumeId; + + @Column(name = "backup_delta_path") + private String backupDeltaPath; + + @Column(name = "backup_parent_path") + private String backupDeltaParentPath; + + public InternalBackupStoragePoolVO() { + } + + public InternalBackupStoragePoolVO(long backupId, long storagePoolId, long volumeId, String backupDeltaPath, String backupDeltaParentPath) { + this.backupId = backupId; + this.storagePoolId = storagePoolId; + this.volumeId = volumeId; + this.backupDeltaPath = backupDeltaPath; + this.backupDeltaParentPath = backupDeltaParentPath; + } + + @Override + public long getId() { + return id; + } + + public long getBackupId() { + return backupId; + } + + public long getStoragePoolId() { + return storagePoolId; + } + + public long getVolumeId() { + return volumeId; + } + + public String getBackupDeltaPath() { + return backupDeltaPath; + } + + public String getBackupDeltaParentPath() { + return backupDeltaParentPath; + } + + public void setBackupDeltaPath(String backupDeltaPath) { + this.backupDeltaPath = backupDeltaPath; + } + + public void setBackupDeltaParentPath(String backupDeltaParentPath) { + this.backupDeltaParentPath = backupDeltaParentPath; + } + + @Override + public String toString() { + return ReflectionToStringBuilder.toString(this, ToStringStyle.JSON_STYLE); + } +} 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 e60e49e1a0c2..3829777d9d8f 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 @@ -42,4 +42,5 @@ public interface BackupDao extends GenericDao { void loadDetails(BackupVO backup); void saveDetails(BackupVO backup); List listBySchedule(Long backupScheduleId); + BackupVO findLatestByStatusAndVmId(Backup.Status status, long vmId); } 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 fd29da72c718..9859f29701b7 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 @@ -90,6 +90,7 @@ protected void init() { backupSearch.and("external_id", backupSearch.entity().getExternalId(), SearchCriteria.Op.EQ); backupSearch.and("backup_offering_id", backupSearch.entity().getBackupOfferingId(), SearchCriteria.Op.EQ); backupSearch.and("zone_id", backupSearch.entity().getZoneId(), SearchCriteria.Op.EQ); + backupSearch.and("status", backupSearch.entity().getStatus(), SearchCriteria.Op.IN); backupSearch.done(); backupVmSearchInZone = createSearchBuilder(Long.class); @@ -280,4 +281,13 @@ public List listVmIdsWithBackupsInZone(Long zoneId) { sc.setParameters("zone_id", zoneId); return customSearchIncludingRemoved(sc, null); } + + @Override + public BackupVO findLatestByStatusAndVmId(Backup.Status status, long vmId) { + SearchCriteria sc = backupSearch.create(); + sc.setParameters("vm_id", vmId); + sc.setParameters("status", status); + Filter filter = new Filter(BackupVO.class, "date", false, 0L, 1L); + return findOneBy(sc, filter); + } } 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 index 664650074bce..c6c8fc3a322e 100644 --- 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 @@ -23,4 +23,19 @@ public interface BackupDetailsDao extends GenericDao, ResourceDetailsDao { + String END_OF_CHAIN = "end_of_chain"; + + String CURRENT = "current"; + + String IMAGE_STORE_ID = "image_store_id"; + + String PARENT_ID = "parent_id"; + + String ISOLATED = "isolated"; + + String SCREENSHOT_PATH = "screenshot_path"; + + String BACKUP_HASH = "backup_hash"; + + void removeDetailsExcept(long backupId, String exception); } 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 index 08c7192af909..5f257c23892c 100644 --- 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 @@ -17,13 +17,39 @@ package org.apache.cloudstack.backup.dao; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; import org.apache.cloudstack.backup.BackupDetailVO; import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; import org.springframework.stereotype.Component; +import javax.annotation.PostConstruct; + @Component public class BackupDetailsDaoImpl extends ResourceDetailsDaoBase implements BackupDetailsDao { + private SearchBuilder backupDetailSearch; + + private static final String BACKUP_ID = "backup_id"; + + private static final String KEY = "key"; + + @PostConstruct + protected void init() { + backupDetailSearch = createSearchBuilder(); + backupDetailSearch.and(BACKUP_ID, backupDetailSearch.entity().getResourceId(), SearchCriteria.Op.EQ); + backupDetailSearch.and(KEY, backupDetailSearch.entity().getName(), SearchCriteria.Op.NEQ); + backupDetailSearch.done(); + } + + @Override + public void removeDetailsExcept(long backupId, String exception) { + SearchCriteria sc = backupDetailSearch.create(); + sc.setParameters(BACKUP_ID, backupId); + sc.setParameters(KEY, exception); + super.expunge(sc); + } + @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/BackupOfferingDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDao.java index d001de8b6c67..d40e828121e0 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDao.java @@ -24,7 +24,7 @@ import com.cloud.utils.db.GenericDao; public interface BackupOfferingDao extends GenericDao { - BackupOfferingResponse newBackupOfferingResponse(BackupOffering policy); + BackupOfferingResponse newBackupOfferingResponse(BackupOffering policy, Boolean crossZoneInstanceCreation); BackupOffering findByExternalId(String externalId, Long zoneId); BackupOffering findByName(String name, Long zoneId); } 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 9d67d07fe5e3..1d7a55d7caf7 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 @@ -20,8 +20,12 @@ import javax.annotation.PostConstruct; import javax.inject.Inject; +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.response.BackupOfferingResponse; import org.apache.cloudstack.backup.BackupOffering; +import org.apache.cloudstack.backup.BackupOfferingDetailsVO; import org.apache.cloudstack.backup.BackupOfferingVO; import com.cloud.dc.DataCenterVO; @@ -30,10 +34,18 @@ import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + public class BackupOfferingDaoImpl extends GenericDaoBase implements BackupOfferingDao { @Inject DataCenterDao dataCenterDao; + @Inject + BackupOfferingDetailsDao backupOfferingDetailsDao; + @Inject + DomainDao domainDao; private SearchBuilder backupPoliciesSearch; @@ -50,9 +62,11 @@ protected void init() { } @Override - public BackupOfferingResponse newBackupOfferingResponse(BackupOffering offering) { - DataCenterVO zone = dataCenterDao.findById(offering.getZoneId()); + public BackupOfferingResponse newBackupOfferingResponse(BackupOffering offering, Boolean crossZoneInstanceCreation) { + DataCenterVO zone = dataCenterDao.findById(offering.getZoneId()); + List domainIds = backupOfferingDetailsDao.findDomainIds(offering.getId()); + List details = backupOfferingDetailsDao.listDetails(offering.getId()); BackupOfferingResponse response = new BackupOfferingResponse(); response.setId(offering.getUuid()); response.setName(offering.getName()); @@ -64,6 +78,29 @@ public BackupOfferingResponse newBackupOfferingResponse(BackupOffering offering) response.setZoneId(zone.getUuid()); response.setZoneName(zone.getName()); } + if (domainIds != null && !domainIds.isEmpty()) { + String domainUUIDs = domainIds.stream().map(Long::valueOf).map(domainId -> { + DomainVO domain = domainDao.findById(domainId); + return domain != null ? domain.getUuid() : ""; + }).filter(name -> !name.isEmpty()).reduce((a, b) -> a + "," + b).orElse(""); + String domainNames = domainIds.stream().map(Long::valueOf).map(domainId -> { + DomainVO domain = domainDao.findById(domainId); + return domain != null ? domain.getName() : ""; + }).filter(name -> !name.isEmpty()).reduce((a, b) -> a + "," + b).orElse(""); + response.setDomain(domainNames); + response.setDomainId(domainUUIDs); + } + if (crossZoneInstanceCreation) { + response.setCrossZoneInstanceCreation(true); + } + details.removeIf(backupOfferingDetailsVO -> ApiConstants.DOMAIN_ID.equals(backupOfferingDetailsVO.getName())); + Map detailString = new HashMap<>(); + for (BackupOfferingDetailsVO detail : details) { + detailString.put(detail.getName(), detail.getValue()); + } + if (!detailString.isEmpty()) { + response.setDetails(detailString); + } response.setCreated(offering.getCreated()); response.setObjectName("backupoffering"); return response; diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDetailsDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDetailsDao.java new file mode 100644 index 000000000000..390fcba1e0e7 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDetailsDao.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 org.apache.cloudstack.backup.dao; + +import java.util.List; + +import org.apache.cloudstack.backup.BackupOfferingDetailsVO; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; + +import com.cloud.utils.db.GenericDao; + +public interface BackupOfferingDetailsDao extends GenericDao, ResourceDetailsDao { + List findDomainIds(final long resourceId); + List findZoneIds(final long resourceId); + String getDetail(Long backupOfferingId, String key); + List findOfferingIdsByDomainIds(List domainIds); + void updateBackupOfferingDomainIdsDetail(long backupOfferingId, List filteredDomainIds); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDetailsDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDetailsDaoImpl.java new file mode 100644 index 000000000000..f052c93f9817 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDetailsDaoImpl.java @@ -0,0 +1,101 @@ +// 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 java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import com.cloud.utils.db.DB; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.backup.BackupOfferingDetailsVO; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; +import org.springframework.stereotype.Component; + +@Component +public class BackupOfferingDetailsDaoImpl extends ResourceDetailsDaoBase implements BackupOfferingDetailsDao { + + @Override + public void addDetail(long resourceId, String key, String value, boolean display) { + super.addDetail(new BackupOfferingDetailsVO(resourceId, key, value, display)); + } + + @Override + public List findDomainIds(long resourceId) { + final List domainIds = new ArrayList<>(); + for (final BackupOfferingDetailsVO detail: findDetails(resourceId, ApiConstants.DOMAIN_ID)) { + final Long domainId = Long.valueOf(detail.getValue()); + if (domainId > 0) { + domainIds.add(domainId); + } + } + return domainIds; + } + + @Override + public List findZoneIds(long resourceId) { + final List zoneIds = new ArrayList<>(); + for (final BackupOfferingDetailsVO detail: findDetails(resourceId, ApiConstants.ZONE_ID)) { + final Long zoneId = Long.valueOf(detail.getValue()); + if (zoneId > 0) { + zoneIds.add(zoneId); + } + } + return zoneIds; + } + + @Override + public String getDetail(Long backupOfferingId, String key) { + String detailValue = null; + BackupOfferingDetailsVO backupOfferingDetail = findDetail(backupOfferingId, key); + if (backupOfferingDetail != null) { + detailValue = backupOfferingDetail.getValue(); + } + return detailValue; + } + + @Override + public List findOfferingIdsByDomainIds(List domainIds) { + Object[] dIds = domainIds.stream().map(s -> String.valueOf(s)).collect(Collectors.toList()).toArray(); + return findResourceIdsByNameAndValueIn("domainid", dIds); + } + + @DB + @Override + public void updateBackupOfferingDomainIdsDetail(long backupOfferingId, List filteredDomainIds) { + SearchBuilder sb = createSearchBuilder(); + List detailsVO = new ArrayList<>(); + sb.and("offeringId", sb.entity().getResourceId(), SearchCriteria.Op.EQ); + sb.and("detailName", sb.entity().getName(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("offeringId", String.valueOf(backupOfferingId)); + sc.setParameters("detailName", ApiConstants.DOMAIN_ID); + remove(sc); + for (Long domainId : filteredDomainIds) { + detailsVO.add(new BackupOfferingDetailsVO(backupOfferingId, ApiConstants.DOMAIN_ID, String.valueOf(domainId), false)); + } + if (!detailsVO.isEmpty()) { + for (BackupOfferingDetailsVO detailVO : detailsVO) { + persist(detailVO); + } + } + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupScheduleDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupScheduleDao.java index ee1783a9c896..87b7dab1ff7b 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupScheduleDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupScheduleDao.java @@ -21,20 +21,14 @@ import java.util.List; import com.cloud.utils.DateUtil; -import org.apache.cloudstack.api.response.BackupScheduleResponse; -import org.apache.cloudstack.backup.BackupSchedule; import org.apache.cloudstack.backup.BackupScheduleVO; import com.cloud.utils.db.GenericDao; public interface BackupScheduleDao extends GenericDao { - BackupScheduleVO findByVM(Long vmId); - List listByVM(Long vmId); BackupScheduleVO findByVMAndIntervalType(Long vmId, DateUtil.IntervalType intervalType); List getSchedulesToExecute(Date currentTimestamp); - - BackupScheduleResponse newBackupScheduleResponse(BackupSchedule schedule); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupScheduleDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupScheduleDaoImpl.java index d9cf7b63680b..972af73391af 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupScheduleDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupScheduleDaoImpl.java @@ -17,28 +17,23 @@ package org.apache.cloudstack.backup.dao; +import java.sql.PreparedStatement; +import java.sql.SQLException; import java.util.Date; import java.util.List; import javax.annotation.PostConstruct; -import javax.inject.Inject; import com.cloud.utils.DateUtil; -import org.apache.cloudstack.api.response.BackupScheduleResponse; -import org.apache.cloudstack.backup.BackupSchedule; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.TransactionLegacy; import org.apache.cloudstack.backup.BackupScheduleVO; 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.vm.dao.VMInstanceDao; public class BackupScheduleDaoImpl extends GenericDaoBase implements BackupScheduleDao { - - @Inject - VMInstanceDao vmInstanceDao; - private SearchBuilder backupScheduleSearch; private SearchBuilder executableSchedulesSearch; @@ -59,13 +54,6 @@ protected void init() { executableSchedulesSearch.done(); } - @Override - public BackupScheduleVO findByVM(Long vmId) { - SearchCriteria sc = backupScheduleSearch.create(); - sc.setParameters("vm_id", vmId); - return findOneBy(sc); - } - @Override public List listByVM(Long vmId) { SearchCriteria sc = backupScheduleSearch.create(); @@ -88,21 +76,19 @@ public List getSchedulesToExecute(Date currentTimestamp) { return listBy(sc); } + @DB @Override - public BackupScheduleResponse newBackupScheduleResponse(BackupSchedule schedule) { - VMInstanceVO vm = vmInstanceDao.findByIdIncludingRemoved(schedule.getVmId()); - BackupScheduleResponse response = new BackupScheduleResponse(); - response.setId(schedule.getUuid()); - response.setVmId(vm.getUuid()); - response.setVmName(vm.getHostName()); - response.setIntervalType(schedule.getScheduleType()); - response.setSchedule(schedule.getSchedule()); - response.setTimezone(schedule.getTimezone()); - response.setMaxBackups(schedule.getMaxBackups()); - if (schedule.getQuiesceVM() != null) { - response.setQuiesceVM(schedule.getQuiesceVM()); + public boolean remove(Long id) { + String sql = "UPDATE backups SET backup_schedule_id = NULL WHERE backup_schedule_id = ?"; + TransactionLegacy transaction = TransactionLegacy.currentTxn(); + try { + PreparedStatement preparedStatement = transaction.prepareAutoCloseStatement(sql); + preparedStatement.setLong(1, id); + preparedStatement.executeUpdate(); + return super.remove(id); + } catch (SQLException e) { + logger.warn("Unable to clean up backup schedules references from the backups table.", e); + return false; } - response.setObjectName("backupschedule"); - return response; } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/ImageTransferDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/ImageTransferDao.java new file mode 100644 index 000000000000..9a9fbeb8814e --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/ImageTransferDao.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 +//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 java.util.List; + +import org.apache.cloudstack.backup.ImageTransfer; +import org.apache.cloudstack.backup.ImageTransferVO; + +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDao; + +public interface ImageTransferDao extends GenericDao { + List listByBackupId(Long backupId); + ImageTransferVO findByUuid(String uuid); + ImageTransferVO findByVolume(Long volumeId); + ImageTransferVO findUnfinishedByVolume(Long volumeId); + List listByPhaseAndDirection(ImageTransfer.Phase phase, ImageTransfer.Direction direction); + List listByZonesAndOwners(List zoneIds, List accountIds, List domainIds, + Filter filter); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/ImageTransferDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/ImageTransferDaoImpl.java new file mode 100644 index 000000000000..2e36f924e4ef --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/ImageTransferDaoImpl.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 +//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 java.util.Collections; +import java.util.List; + +import javax.annotation.PostConstruct; + +import org.apache.cloudstack.backup.ImageTransfer; +import org.apache.cloudstack.backup.ImageTransferVO; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Component; + +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@Component +public class ImageTransferDaoImpl extends GenericDaoBase implements ImageTransferDao { + + private SearchBuilder backupIdSearch; + private SearchBuilder uuidSearch; + private SearchBuilder volumeSearch; + private SearchBuilder volumeUnfinishedSearch; + private SearchBuilder phaseDirectionSearch; + + public ImageTransferDaoImpl() { + } + + @PostConstruct + protected void init() { + backupIdSearch = createSearchBuilder(); + backupIdSearch.and("backupId", backupIdSearch.entity().getBackupId(), SearchCriteria.Op.EQ); + backupIdSearch.done(); + + uuidSearch = createSearchBuilder(); + uuidSearch.and("uuid", uuidSearch.entity().getUuid(), SearchCriteria.Op.EQ); + uuidSearch.done(); + + volumeSearch = createSearchBuilder(); + volumeSearch.and("volumeId", volumeSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); + volumeSearch.done(); + + volumeUnfinishedSearch = createSearchBuilder(); + volumeUnfinishedSearch.and("volumeId", volumeUnfinishedSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); + volumeUnfinishedSearch.and("phase", volumeUnfinishedSearch.entity().getPhase(), SearchCriteria.Op.NEQ); + volumeUnfinishedSearch.done(); + + phaseDirectionSearch = createSearchBuilder(); + phaseDirectionSearch.and("phase", phaseDirectionSearch.entity().getPhase(), SearchCriteria.Op.EQ); + phaseDirectionSearch.and("direction", phaseDirectionSearch.entity().getDirection(), SearchCriteria.Op.EQ); + phaseDirectionSearch.done(); + } + + @Override + public List listByBackupId(Long backupId) { + SearchCriteria sc = backupIdSearch.create(); + sc.setParameters("backupId", backupId); + return listBy(sc); + } + + @Override + public ImageTransferVO findByUuid(String uuid) { + SearchCriteria sc = uuidSearch.create(); + sc.setParameters("uuid", uuid); + return findOneBy(sc); + } + + @Override + public ImageTransferVO findByVolume(Long volumeId) { + SearchCriteria sc = volumeSearch.create(); + sc.setParameters("volumeId", volumeId); + return findOneBy(sc); + } + + @Override + public ImageTransferVO findUnfinishedByVolume(Long volumeId) { + SearchCriteria sc = volumeUnfinishedSearch.create(); + sc.setParameters("volumeId", volumeId); + sc.setParameters("phase", ImageTransferVO.Phase.finished.toString()); + return findOneBy(sc); + } + + @Override + public List listByPhaseAndDirection(ImageTransfer.Phase phase, ImageTransfer.Direction direction) { + SearchCriteria sc = phaseDirectionSearch.create(); + sc.setParameters("phase", phase); + sc.setParameters("direction", direction); + return listBy(sc); + } + + @Override + public List listByZonesAndOwners(List zoneIds, List accountIds, List domainIds, + Filter filter) { + if (CollectionUtils.isEmpty(zoneIds)) { + return Collections.emptyList(); + } + SearchBuilder sb = createSearchBuilder(); + sb.and("dataCenterId", sb.entity().getDataCenterId(), SearchCriteria.Op.IN); + boolean accountIdsNotEmpty = CollectionUtils.isNotEmpty(accountIds); + boolean domainIdsNotEmpty = CollectionUtils.isNotEmpty(domainIds); + if (accountIdsNotEmpty || domainIdsNotEmpty) { + sb.and().op("account", sb.entity().getAccountId(), SearchCriteria.Op.IN); + sb.or("domain", sb.entity().getDomainId(), SearchCriteria.Op.IN); + sb.cp(); + } + sb.done(); + final SearchCriteria sc = sb.create(); + sc.setParameters("dataCenterId", zoneIds.toArray()); + if (accountIdsNotEmpty) { + sc.setParameters("account", accountIds.toArray()); + } + if (domainIdsNotEmpty) { + sc.setParameters("domain", domainIds.toArray()); + } + + return listBy(sc, filter); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupDataStoreDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupDataStoreDao.java new file mode 100644 index 000000000000..e71ffe5ebde9 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupDataStoreDao.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 +//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 com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.backup.InternalBackupDataStoreVO; + +import java.util.List; + +public interface InternalBackupDataStoreDao extends GenericDao { + + List listByBackupId(long backupId); + + InternalBackupDataStoreVO findByBackupIdAndVolumeId(long backupId, long volumeId); + + void expungeByBackupId(long backupId); + + void updateVolumeId(long oldVolumeId, long newVolumeId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupDataStoreDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupDataStoreDaoImpl.java new file mode 100644 index 000000000000..34a064ff62a2 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupDataStoreDaoImpl.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 +//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 com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.UpdateBuilder; +import org.apache.cloudstack.backup.InternalBackupDataStoreVO; + +import javax.annotation.PostConstruct; +import java.util.List; + +public class InternalBackupDataStoreDaoImpl extends GenericDaoBase implements InternalBackupDataStoreDao { + + private SearchBuilder backupSearch; + + private static final String BACKUP_ID = "backup_id"; + private static final String VOLUME_ID = "volume_id"; + + @PostConstruct + protected void init() { + backupSearch = createSearchBuilder(); + backupSearch.and(BACKUP_ID, backupSearch.entity().getBackupId(), SearchCriteria.Op.EQ); + backupSearch.and(VOLUME_ID, backupSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); + backupSearch.done(); + } + + @Override + public List listByBackupId(long backupId) { + SearchCriteria sc = backupSearch.create(); + sc.setParameters(BACKUP_ID, backupId); + return listBy(sc); + } + + @Override + public InternalBackupDataStoreVO findByBackupIdAndVolumeId(long backupId, long volumeId) { + SearchCriteria sc = backupSearch.create(); + sc.setParameters(BACKUP_ID, backupId); + sc.setParameters(VOLUME_ID, volumeId); + return findOneBy(sc); + } + + @Override + public void expungeByBackupId(long backupId) { + SearchCriteria sc = backupSearch.create(); + sc.setParameters(BACKUP_ID, backupId); + expunge(sc); + } + + @Override + public void updateVolumeId(long oldVolumeId, long newVolumeId) { + SearchCriteria sc = backupSearch.create(); + sc.setParameters(VOLUME_ID, oldVolumeId); + InternalBackupDataStoreVO delta = createForUpdate(); + delta.setVolumeId(newVolumeId); + UpdateBuilder ub = getUpdateBuilder(delta); + update(ub, sc, null); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupJoinDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupJoinDao.java new file mode 100644 index 000000000000..2f9c3dd1ff8f --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupJoinDao.java @@ -0,0 +1,40 @@ +//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.backup.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.backup.InternalBackupJoinVO; + +import java.util.Date; +import java.util.List; + +public interface InternalBackupJoinDao extends GenericDao { + + List listByBackedUpAndVmIdAndDateBeforeOrAfterOrderBy(long vmId, Date date, boolean before, boolean ascending); + + List listIncludingRemovedByVmIdAndBeforeDateOrderByCreatedDesc(long vmId, Date beforeDate); + + InternalBackupJoinVO findCurrent(long vmId); + + InternalBackupJoinVO findByParentId(long parentId); + + List listByImageStoreId(long imageStoreId); + + List listById(long id); + + List listByParentId(long parentId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupJoinDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupJoinDaoImpl.java new file mode 100644 index 000000000000..e8d823bb69c5 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupJoinDaoImpl.java @@ -0,0 +1,130 @@ +// 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 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.backup.Backup; +import org.apache.cloudstack.backup.InternalBackupJoinVO; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class InternalBackupJoinDaoImpl extends GenericDaoBase implements InternalBackupJoinDao { + + private static final String ID = "id"; + private static final String VM_ID = "vm_id"; + private static final String STATUS = "status"; + private static final String CREATED_BEFORE = "created_before"; + private static final String CREATED_AFTER = "created_after"; + private static final String CURRENT = "current"; + private static final String ISOLATED = "isolated"; + private static final String PARENT_ID = "parent_id"; + private static final String IMAGE_STORE_ID = "image_store_id"; + private SearchBuilder backupSearch; + private SearchBuilder allBackupsSearch; + + @PostConstruct + protected void init() { + backupSearch = createSearchBuilder(); + backupSearch.and(VM_ID, backupSearch.entity().getVmId(), SearchCriteria.Op.EQ); + backupSearch.and(STATUS, backupSearch.entity().getStatus(), SearchCriteria.Op.IN); + backupSearch.and(CREATED_BEFORE, backupSearch.entity().getDate(), SearchCriteria.Op.LT); + backupSearch.and(CREATED_AFTER, backupSearch.entity().getDate(), SearchCriteria.Op.GT); + backupSearch.and(CURRENT, backupSearch.entity().getCurrent(), SearchCriteria.Op.EQ); + backupSearch.and(PARENT_ID, backupSearch.entity().getParentId(), SearchCriteria.Op.EQ); + backupSearch.and(ISOLATED, backupSearch.entity().getIsolated(), SearchCriteria.Op.EQ); + backupSearch.groupBy(backupSearch.entity().getId()); + backupSearch.done(); + + allBackupsSearch = createSearchBuilder(); + allBackupsSearch.and(ID, allBackupsSearch.entity().getId(), SearchCriteria.Op.EQ); + allBackupsSearch.and(STATUS, allBackupsSearch.entity().getStatus(), SearchCriteria.Op.IN); + allBackupsSearch.and(PARENT_ID, allBackupsSearch.entity().getParentId(), SearchCriteria.Op.EQ); + allBackupsSearch.and(IMAGE_STORE_ID, allBackupsSearch.entity().getImageStoreId(), SearchCriteria.Op.EQ); + allBackupsSearch.done(); + } + + @Override + public List listByBackedUpAndVmIdAndDateBeforeOrAfterOrderBy(long vmId, Date date, boolean before, boolean ascending) { + SearchCriteria sc = backupSearch.create(); + sc.setParameters(VM_ID, vmId); + sc.setParameters(STATUS, Backup.Status.BackedUp); + if (before) { + sc.setParameters(CREATED_BEFORE, date); + } else { + sc.setParameters(CREATED_AFTER, date); + } + sc.setParameters(ISOLATED, Boolean.FALSE.toString()); + Filter filter = new Filter(InternalBackupJoinVO.class, "date", ascending); + return new ArrayList<>(listBy(sc, filter)); + } + + @Override + public List listIncludingRemovedByVmIdAndBeforeDateOrderByCreatedDesc(long vmId, Date beforeDate) { + SearchCriteria sc = backupSearch.create(); + sc.setParameters(VM_ID, vmId); + sc.setParameters(STATUS, Backup.Status.BackedUp, Backup.Status.Removed); + sc.setParameters(CREATED_BEFORE, beforeDate); + sc.setParameters(ISOLATED, Boolean.FALSE.toString()); + Filter filter = new Filter(InternalBackupJoinVO.class, "date", false); + return new ArrayList<>(listIncludingRemovedBy(sc, filter)); + } + + @Override + public InternalBackupJoinVO findCurrent(long vmId) { + SearchCriteria sc = backupSearch.create(); + sc.setParameters(VM_ID, vmId); + sc.setParameters(CURRENT, Boolean.TRUE.toString()); + return findOneBy(sc); + } + + @Override + public InternalBackupJoinVO findByParentId(long parentId) { + SearchCriteria sc = backupSearch.create(); + sc.setParameters(PARENT_ID, parentId); + return findOneIncludingRemovedBy(sc); + } + + @Override + public List listByImageStoreId(long imageStoreId) { + SearchCriteria sc = allBackupsSearch.create(); + sc.setParameters(IMAGE_STORE_ID, imageStoreId); + sc.setParameters(STATUS, Backup.Status.BackedUp); + return listBy(sc); + } + + @Override + public List listById(long id) { + SearchCriteria sc = allBackupsSearch.create(); + sc.setParameters(ID, id); + sc.setParameters(STATUS, Backup.Status.BackedUp); + return listBy(sc); + } + + @Override + public List listByParentId(long parentId) { + SearchCriteria sc = allBackupsSearch.create(); + sc.setParameters(PARENT_ID, parentId); + sc.setParameters(STATUS, Backup.Status.BackedUp); + return listBy(sc); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupServiceJobDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupServiceJobDao.java new file mode 100644 index 000000000000..c80734b9cb3c --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupServiceJobDao.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 +//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 com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.backup.InternalBackupServiceJobType; +import org.apache.cloudstack.backup.InternalBackupServiceJobVO; + +import java.util.Date; +import java.util.List; + +public interface InternalBackupServiceJobDao extends GenericDao { + + List listExecutingJobsByZoneIdAndJobType(long zoneId, InternalBackupServiceJobType... jobTypes); + + List listWaitingJobsAndScheduledToBeforeNow(long zoneId, InternalBackupServiceJobType... jobTypes); + + List listExecutingJobsByHostsAndStartTimeBeforeAndTypeIn(Object[] hostIds, Date date, InternalBackupServiceJobType... jobTypes); + + Pair, Integer> searchAndCountForListApi(Long id, Long backupId, Long hostId, Long zoneId, InternalBackupServiceJobType type, boolean executing, + boolean scheduled, Long startIndex, Long pageSize); + + void update(InternalBackupServiceJobVO job); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupServiceJobDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupServiceJobDaoImpl.java new file mode 100644 index 000000000000..32e81261838d --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupServiceJobDaoImpl.java @@ -0,0 +1,138 @@ +//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.backup.dao; + +import com.cloud.utils.DateUtil; +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.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.apache.cloudstack.backup.InternalBackupServiceJobType; +import org.apache.cloudstack.backup.InternalBackupServiceJobVO; + +import javax.annotation.PostConstruct; +import java.util.Date; +import java.util.List; + +public class InternalBackupServiceJobDaoImpl extends GenericDaoBase implements InternalBackupServiceJobDao { + private static final String ID = "id"; + private static final String BACKUP_ID = "backup_id"; + private SearchBuilder executingBeforeAndHostInAndTypeInSearch; + private SearchBuilder scheduledAndNotStartedSearch; + + private SearchBuilder executingAndZoneIdAndTypeSearch; + + private static final String HOST_ID = "host_id"; + private static final String TYPE = "type"; + private static final String START_TIME = "start_time"; + private static final String SCHEDULED = "scheduled"; + private static final String ZONE_ID = "zone_id"; + + @PostConstruct + protected void init() { + executingBeforeAndHostInAndTypeInSearch = createSearchBuilder(); + executingBeforeAndHostInAndTypeInSearch.and(HOST_ID, executingBeforeAndHostInAndTypeInSearch.entity().getHostId(), SearchCriteria.Op.IN); + executingBeforeAndHostInAndTypeInSearch.and(START_TIME, executingBeforeAndHostInAndTypeInSearch.entity().getStartTime(), SearchCriteria.Op.LTEQ); + executingBeforeAndHostInAndTypeInSearch.and(TYPE, executingBeforeAndHostInAndTypeInSearch.entity().getType(), SearchCriteria.Op.IN); + executingBeforeAndHostInAndTypeInSearch.done(); + + scheduledAndNotStartedSearch = createSearchBuilder(); + scheduledAndNotStartedSearch.and(SCHEDULED, scheduledAndNotStartedSearch.entity().getScheduledStartTime(), SearchCriteria.Op.LTEQ); + scheduledAndNotStartedSearch.and(START_TIME, scheduledAndNotStartedSearch.entity().getStartTime(), SearchCriteria.Op.NULL); + scheduledAndNotStartedSearch.and(ZONE_ID, scheduledAndNotStartedSearch.entity().getZoneId(), SearchCriteria.Op.EQ); + scheduledAndNotStartedSearch.and(TYPE, scheduledAndNotStartedSearch.entity().getType(), SearchCriteria.Op.IN); + scheduledAndNotStartedSearch.done(); + + executingAndZoneIdAndTypeSearch = createSearchBuilder(); + executingAndZoneIdAndTypeSearch.and(START_TIME, executingAndZoneIdAndTypeSearch.entity().getStartTime(), SearchCriteria.Op.NNULL); + executingAndZoneIdAndTypeSearch.and(ZONE_ID, executingAndZoneIdAndTypeSearch.entity().getZoneId(), SearchCriteria.Op.EQ); + executingAndZoneIdAndTypeSearch.and(TYPE, executingAndZoneIdAndTypeSearch.entity().getType(), SearchCriteria.Op.IN); + executingAndZoneIdAndTypeSearch.done(); + } + + @Override + public List listExecutingJobsByZoneIdAndJobType(long zoneId, InternalBackupServiceJobType... jobTypes) { + SearchCriteria sc = executingAndZoneIdAndTypeSearch.create(); + sc.setParameters(TYPE, (Object[]) jobTypes); + sc.setParameters(ZONE_ID, zoneId); + + return listBy(sc); + } + + @Override + public List listWaitingJobsAndScheduledToBeforeNow(long zoneId, InternalBackupServiceJobType... jobTypes) { + SearchCriteria sc = scheduledAndNotStartedSearch.create(); + + sc.setParameters(SCHEDULED, DateUtil.now()); + sc.setParameters(ZONE_ID, zoneId); + sc.setParameters(TYPE, (Object[]) jobTypes); + + Filter filter = new Filter(InternalBackupServiceJobVO.class, "scheduledStartTime", true); + return listBy(sc, filter); + } + + @Override + public List listExecutingJobsByHostsAndStartTimeBeforeAndTypeIn(Object[] hostIds, Date date, InternalBackupServiceJobType... jobTypes) { + SearchCriteria sc = executingBeforeAndHostInAndTypeInSearch.create(); + sc.setParameters(HOST_ID, hostIds); + sc.setParameters(START_TIME, date); + sc.setParameters(TYPE, (Object[]) jobTypes); + + return listBy(sc); + } + + @Override + public Pair, Integer> searchAndCountForListApi(Long id, Long backupId, Long hostId, Long zoneId, InternalBackupServiceJobType type, boolean executing, + boolean scheduled, Long startIndex, Long pageSize) { + SearchBuilder sb = createSearchBuilder(); + + sb.and(ID, sb.entity().getId(), SearchCriteria.Op.EQ); + sb.and(BACKUP_ID, sb.entity().getBackupId(), SearchCriteria.Op.EQ); + sb.and(HOST_ID, sb.entity().getHostId(), SearchCriteria.Op.EQ); + sb.and(ZONE_ID, sb.entity().getZoneId(), SearchCriteria.Op.EQ); + sb.and(TYPE, sb.entity().getType(), SearchCriteria.Op.EQ); + + boolean removed = !executing && !scheduled; + if (executing && !scheduled) { + sb.and("executing", sb.entity().getStartTime(), SearchCriteria.Op.NNULL); + } else if (scheduled && !executing) { + sb.and("scheduled", sb.entity().getStartTime(), SearchCriteria.Op.NULL); + } + + SearchCriteria sc = sb.create(); + + sc.setParametersIfNotNull(ID, id); + sc.setParametersIfNotNull(BACKUP_ID, backupId); + sc.setParametersIfNotNull(HOST_ID, hostId); + sc.setParametersIfNotNull(ZONE_ID, zoneId); + if (type != null) { + sc.setParameters(TYPE, type); + } + + Filter filter = new Filter(InternalBackupServiceJobVO.class, "created", false, startIndex, pageSize); + + return searchAndCount(sc, filter, removed); + } + + @Override + @DB + public void update(InternalBackupServiceJobVO job) { + super.update(job.getId(), job); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupStoragePoolDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupStoragePoolDao.java new file mode 100644 index 000000000000..7e2ac5a249e4 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupStoragePoolDao.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 +//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 com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.backup.InternalBackupStoragePoolVO; + +import java.util.List; + +public interface InternalBackupStoragePoolDao extends GenericDao { + + List listByBackupId(long backupId); + + InternalBackupStoragePoolVO findOneByVolumeId(long volumeId); + + void expungeByBackupId(long backupId); + + void expungeByVolumeId(long volumeId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupStoragePoolDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupStoragePoolDaoImpl.java new file mode 100644 index 000000000000..443ceed02e7d --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupStoragePoolDaoImpl.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 +//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 com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.apache.cloudstack.backup.InternalBackupStoragePoolVO; + +import javax.annotation.PostConstruct; +import java.util.List; + +public class InternalBackupStoragePoolDaoImpl extends GenericDaoBase implements InternalBackupStoragePoolDao { + + private SearchBuilder backupSearch; + + private static final String BACKUP_ID = "backup_id"; + + private static final String VOLUME_ID = "volume_id"; + + @PostConstruct + protected void init() { + backupSearch = createSearchBuilder(); + backupSearch.and(BACKUP_ID, backupSearch.entity().getBackupId(), SearchCriteria.Op.EQ); + backupSearch.and(VOLUME_ID, backupSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); + backupSearch.done(); + } + + @Override + public List listByBackupId(long backupId) { + SearchCriteria sc = backupSearch.create(); + sc.setParameters(BACKUP_ID, backupId); + return listBy(sc); + } + + @Override + public InternalBackupStoragePoolVO findOneByVolumeId(long volumeId) { + SearchCriteria sc = backupSearch.create(); + sc.setParameters(VOLUME_ID, volumeId); + return findOneBy(sc); + } + + @Override + public void expungeByBackupId(long backupId) { + SearchCriteria sc = backupSearch.create(); + sc.setParameters(BACKUP_ID, backupId); + expunge(sc); + } + + @Override + public void expungeByVolumeId(long volumeId) { + SearchCriteria sc = backupSearch.create(); + sc.setParameters(VOLUME_ID, volumeId); + expunge(sc); + } +} 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 index 2df23b3d1064..45bad5a44d79 100644 --- 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 @@ -63,6 +63,9 @@ public class GuiThemeJoinVO implements GuiThemeJoin { @Column(name = "is_public") private boolean isPublic; + @Column(name = "login_base_domain") + private String loginBaseDomain; + @Column(name = GenericDao.CREATED_COLUMN, nullable = false) @Temporal(value = TemporalType.TIMESTAMP) private Date created; @@ -138,4 +141,9 @@ public Date getCreated() { public Date getRemoved() { return removed; } + + @Override + public String getLoginBaseDomain() { + return loginBaseDomain; + } } 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 index 887e3886f6c6..729071f60b80 100644 --- 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 @@ -59,6 +59,9 @@ public class GuiThemeVO implements GuiTheme { @Column(name = "recursive_domains") private boolean recursiveDomains = false; + @Column(name = "login_base_domain", length = 65535) + private String loginBaseDomain; + @Column(name = GenericDao.CREATED_COLUMN, nullable = false) @Temporal(value = TemporalType.TIMESTAMP) private Date created; @@ -71,7 +74,8 @@ public GuiThemeVO() { } - public GuiThemeVO(String name, String description, String css, String jsonConfiguration, boolean recursiveDomains, boolean isPublic, Date created, Date removed) { + public GuiThemeVO(String name, String description, String css, String jsonConfiguration, boolean recursiveDomains, + boolean isPublic, Date created, String loginBaseDomain, Date removed) { this.name = name; this.description = description; this.css = css; @@ -79,6 +83,7 @@ public GuiThemeVO(String name, String description, String css, String jsonConfig this.recursiveDomains = recursiveDomains; this.isPublic = isPublic; this.created = created; + this.loginBaseDomain = loginBaseDomain; this.removed = removed; } @@ -186,4 +191,8 @@ public void setRecursiveDomains(boolean recursiveDomains) { public String toString() { return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "uuid", "name", "description", "isPublic", "recursiveDomains"); } + + public void setLoginBaseDomain(String loginBaseDomain) { + this.loginBaseDomain = loginBaseDomain; + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/kms/HSMProfileDetailsVO.java b/engine/schema/src/main/java/org/apache/cloudstack/kms/HSMProfileDetailsVO.java new file mode 100644 index 000000000000..cd20b8a74fe5 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/kms/HSMProfileDetailsVO.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.kms; + +import org.apache.cloudstack.api.ResourceDetail; + +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 = "kms_hsm_profile_details") +public class HSMProfileDetailsVO implements ResourceDetail { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "profile_id") + private long resourceId; + + @Column(name = "name") + private String name; + + @Column(name = "value") + private String value; + + public HSMProfileDetailsVO() { + } + + public HSMProfileDetailsVO(long profileId, String name, String value) { + this.resourceId = profileId; + this.name = name; + this.value = value; + } + + @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 true; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/kms/HSMProfileVO.java b/engine/schema/src/main/java/org/apache/cloudstack/kms/HSMProfileVO.java new file mode 100644 index 000000000000..86d57400c0d2 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/kms/HSMProfileVO.java @@ -0,0 +1,183 @@ +// 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.kms; + +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 = "kms_hsm_profiles") +public class HSMProfileVO implements HSMProfile { + + @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 = "protocol") + private String protocol; + + @Column(name = "account_id") + private Long accountId; + + @Column(name = "domain_id") + private Long domainId; + + @Column(name = "zone_id") + private Long zoneId; + + @Column(name = "vendor_name") + private String vendorName; + + @Column(name = "enabled") + private boolean enabled; + + @Column(name = "is_public") + private boolean isPublic; + + @Column(name = "created") + private Date created; + + @Column(name = "removed") + private Date removed; + + public HSMProfileVO() { + this.uuid = UUID.randomUUID().toString(); + this.created = new Date(); + this.isPublic = false; + } + + public HSMProfileVO(String name, String protocol, Long accountId, Long domainId, Long zoneId, String vendorName) { + this.uuid = UUID.randomUUID().toString(); + this.name = name; + this.protocol = protocol; + this.accountId = accountId; + this.domainId = domainId; + this.zoneId = zoneId; + this.vendorName = vendorName; + this.enabled = true; + this.isPublic = false; + this.created = new Date(); + } + + @Override + public String toString() { + return String.format("HSMProfileVO %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "protocol", "system", "enabled")); + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getProtocol() { + return protocol; + } + + @Override + public long getAccountId() { + return accountId == null ? -1 : accountId; + } + + @Override + public long getDomainId() { + return domainId == null ? -1 : domainId; + } + + @Override + public Long getZoneId() { + return zoneId; + } + + @Override + public String getVendorName() { + return vendorName; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public Date getCreated() { + return created; + } + + @Override + public Date getRemoved() { + return removed; + } + + @Override + public Class getEntityType() { + return HSMProfile.class; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public void setVendorName(String vendorName) { + this.vendorName = vendorName; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public boolean getIsPublic() { + return isPublic; + } + + public void setIsPublic(boolean isPublic) { + this.isPublic = isPublic; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/kms/KMSKekVersionVO.java b/engine/schema/src/main/java/org/apache/cloudstack/kms/KMSKekVersionVO.java new file mode 100644 index 000000000000..1dacdff68027 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/kms/KMSKekVersionVO.java @@ -0,0 +1,193 @@ +// 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.kms; + +import com.cloud.utils.db.GenericDao; +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 javax.persistence.Temporal; +import javax.persistence.TemporalType; +import java.util.Date; +import java.util.UUID; + +/** + * Database entity for KEK versions. + * Tracks multiple KEK versions per KMS key to support gradual rotation. + * During rotation, a new version is created (status=Active) and old versions + * are marked as Previous (still usable for decryption) or Archived (no longer used). + */ +@Entity +@Table(name = "kms_kek_versions") +public class KMSKekVersionVO { + + public enum Status { + /** + * Used for new encryption operations + */ + Active, + /** + * Still usable for decryption during key rotation + */ + Previous, + /** + * No longer used; all wrapped keys have been re-encrypted + */ + Archived + } + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "uuid", nullable = false) + private String uuid; + + @Column(name = "kms_key_id", nullable = false) + private Long kmsKeyId; + + @Column(name = "version_number", nullable = false) + private Integer versionNumber; + + @Column(name = "kek_label", nullable = false) + private String kekLabel; + + @Column(name = "status", nullable = false, length = 32) + @Enumerated(EnumType.STRING) + private Status status; + + @Column(name = "hsm_profile_id") + private Long hsmProfileId; + + @Column(name = GenericDao.CREATED_COLUMN, nullable = false) + @Temporal(TemporalType.TIMESTAMP) + private Date created; + + @Column(name = GenericDao.REMOVED_COLUMN) + @Temporal(TemporalType.TIMESTAMP) + private Date removed; + + public KMSKekVersionVO(Long kmsKeyId, Integer versionNumber, String kekLabel) { + this(); + this.kmsKeyId = kmsKeyId; + this.versionNumber = versionNumber; + this.kekLabel = kekLabel; + this.status = Status.Active; + } + + public KMSKekVersionVO(Long kmsKeyId, String kekLabel) { + this(); + this.kmsKeyId = kmsKeyId; + this.kekLabel = kekLabel; + this.status = Status.Active; + } + + public KMSKekVersionVO() { + this.uuid = UUID.randomUUID().toString(); + this.created = new Date(); + this.status = Status.Active; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public Long getKmsKeyId() { + return kmsKeyId; + } + + public void setKmsKeyId(Long kmsKeyId) { + this.kmsKeyId = kmsKeyId; + } + + public Integer getVersionNumber() { + return versionNumber; + } + + public void setVersionNumber(Integer versionNumber) { + this.versionNumber = versionNumber; + } + + public String getKekLabel() { + return kekLabel; + } + + public void setKekLabel(String kekLabel) { + this.kekLabel = kekLabel; + } + + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } + + public Long getHsmProfileId() { + return hsmProfileId; + } + + public void setHsmProfileId(Long hsmProfileId) { + this.hsmProfileId = hsmProfileId; + } + + + 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; + } + + @Override + public String toString() { + return String.format("KMSKekVersion %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "kmsKeyId", "versionNumber", "status", "kekLabel")); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/kms/KMSKeyVO.java b/engine/schema/src/main/java/org/apache/cloudstack/kms/KMSKeyVO.java new file mode 100644 index 000000000000..36c68de3744b --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/kms/KMSKeyVO.java @@ -0,0 +1,264 @@ +// 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.kms; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.framework.kms.KeyPurpose; +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 javax.persistence.Temporal; +import javax.persistence.TemporalType; +import java.util.Date; +import java.util.UUID; + +/** + * Database entity for KMS Key (Key Encryption Key) metadata. + * Tracks ownership, purpose, and lifecycle of KEKs used in envelope encryption. + */ +@Entity +@Table(name = "kms_keys") +public class KMSKeyVO implements KMSKey { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "uuid", nullable = false) + private String uuid; + + @Column(name = "name", nullable = false) + private String name; + + @Column(name = "description", length = 1024) + private String description; + + @Column(name = "kek_label", nullable = false) + private String kekLabel; + + @Column(name = "purpose", nullable = false, length = 32) + @Enumerated(EnumType.STRING) + private KeyPurpose purpose; + + @Column(name = "account_id", nullable = false) + private Long accountId; + + @Column(name = "domain_id", nullable = false) + private Long domainId; + + @Column(name = "zone_id", nullable = false) + private Long zoneId; + + @Column(name = "algorithm", nullable = false, length = 64) + private String algorithm; + + @Column(name = "key_bits", nullable = false) + private Integer keyBits; + + @Column(name = "enabled", nullable = false) + private boolean enabled; + + @Column(name = "hsm_profile_id") + private Long hsmProfileId; + + @Column(name = GenericDao.CREATED_COLUMN, nullable = false) + @Temporal(TemporalType.TIMESTAMP) + private Date created; + + @Column(name = GenericDao.REMOVED_COLUMN) + @Temporal(TemporalType.TIMESTAMP) + private Date removed; + + public KMSKeyVO(String name, String description, String kekLabel, + KeyPurpose purpose, Long accountId, Long domainId, + Long zoneId, String algorithm, Integer keyBits + ) { + this(); + this.name = name; + this.description = description; + this.kekLabel = kekLabel; + this.purpose = purpose; + this.accountId = accountId; + this.domainId = domainId; + this.zoneId = zoneId; + this.algorithm = algorithm; + this.keyBits = keyBits; + } + + public KMSKeyVO() { + this.uuid = UUID.randomUUID().toString(); + this.created = new Date(); + this.enabled = true; + } + + @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; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String getKekLabel() { + return kekLabel; + } + + @Override + public KeyPurpose getPurpose() { + return purpose; + } + + @Override + public Long getZoneId() { + return zoneId; + } + + @Override + public String getAlgorithm() { + return algorithm; + } + + @Override + public Integer getKeyBits() { + return keyBits; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public Date getCreated() { + return created; + } + + @Override + public Date getRemoved() { + return removed; + } + + @Override + public Long getHsmProfileId() { + return hsmProfileId; + } + + public void setHsmProfileId(Long hsmProfileId) { + this.hsmProfileId = hsmProfileId; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } + + public void setCreated(Date created) { + this.created = created; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public void setKeyBits(Integer keyBits) { + this.keyBits = keyBits; + } + + public void setAlgorithm(String algorithm) { + this.algorithm = algorithm; + } + + public void setZoneId(Long zoneId) { + this.zoneId = zoneId; + } + + public void setPurpose(KeyPurpose purpose) { + this.purpose = purpose; + } + + public void setKekLabel(String kekLabel) { + this.kekLabel = kekLabel; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public long getAccountId() { + return accountId; + } + + public void setAccountId(Long accountId) { + this.accountId = accountId; + } + + @Override + public long getDomainId() { + return domainId; + } + + public void setDomainId(Long domainId) { + this.domainId = domainId; + } + + @Override + public Class getEntityType() { + return KMSKey.class; + } + + @Override + public String toString() { + return String.format("KMSKey %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "purpose", + "accountId", "zoneId", "enabled" + )); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/kms/KMSWrappedKeyVO.java b/engine/schema/src/main/java/org/apache/cloudstack/kms/KMSWrappedKeyVO.java new file mode 100644 index 000000000000..8a763623fba7 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/kms/KMSWrappedKeyVO.java @@ -0,0 +1,176 @@ +// 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.kms; + +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.Arrays; +import java.util.Date; +import java.util.UUID; + +/** + * Database entity for storing wrapped (encrypted) Data Encryption Keys. + * Each entry represents a DEK that has been encrypted by a Key Encryption Key (KEK). + * KEK metadata is stored in kms_keys table via the kms_key_id foreign key. + */ +@Entity +@Table(name = "kms_wrapped_key") +public class KMSWrappedKeyVO { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "uuid", nullable = false) + private String uuid; + + @Column(name = "kms_key_id") + private Long kmsKeyId; + + @Column(name = "kek_version_id") + private Long kekVersionId; + + @Column(name = "zone_id", nullable = false) + private Long zoneId; + + @Column(name = "wrapped_blob", nullable = false) + private byte[] wrappedBlob; + + @Column(name = GenericDao.CREATED_COLUMN, nullable = false) + @Temporal(TemporalType.TIMESTAMP) + private Date created; + + @Column(name = GenericDao.REMOVED_COLUMN) + @Temporal(TemporalType.TIMESTAMP) + private Date removed; + + public KMSWrappedKeyVO(KMSKeyVO kmsKey, byte[] wrappedBlob) { + this(); + this.kmsKeyId = kmsKey.getId(); + this.zoneId = kmsKey.getZoneId(); + this.wrappedBlob = wrappedBlob != null ? Arrays.copyOf(wrappedBlob, wrappedBlob.length) : null; + } + + public KMSWrappedKeyVO() { + this.uuid = UUID.randomUUID().toString(); + this.created = new Date(); + } + + public KMSWrappedKeyVO(KMSKeyVO kmsKey, Long kekVersionId, byte[] wrappedBlob) { + this(); + this.kmsKeyId = kmsKey.getId(); + this.kekVersionId = kekVersionId; + this.zoneId = kmsKey.getZoneId(); + this.wrappedBlob = wrappedBlob != null ? Arrays.copyOf(wrappedBlob, wrappedBlob.length) : null; + } + + public KMSWrappedKeyVO(Long kmsKeyId, Long zoneId, byte[] wrappedBlob) { + this(); + this.kmsKeyId = kmsKeyId; + this.zoneId = zoneId; + this.wrappedBlob = wrappedBlob != null ? Arrays.copyOf(wrappedBlob, wrappedBlob.length) : null; + } + + public KMSWrappedKeyVO(Long kmsKeyId, Long kekVersionId, Long zoneId, byte[] wrappedBlob) { + this(); + this.kmsKeyId = kmsKeyId; + this.kekVersionId = kekVersionId; + this.zoneId = zoneId; + this.wrappedBlob = wrappedBlob != null ? Arrays.copyOf(wrappedBlob, wrappedBlob.length) : null; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public Long getKmsKeyId() { + return kmsKeyId; + } + + public void setKmsKeyId(Long kmsKeyId) { + this.kmsKeyId = kmsKeyId; + } + + public Long getKekVersionId() { + return kekVersionId; + } + + public void setKekVersionId(Long kekVersionId) { + this.kekVersionId = kekVersionId; + } + + public Long getZoneId() { + return zoneId; + } + + public void setZoneId(Long zoneId) { + this.zoneId = zoneId; + } + + public byte[] getWrappedBlob() { + return wrappedBlob != null ? Arrays.copyOf(wrappedBlob, wrappedBlob.length) : null; + } + + public void setWrappedBlob(byte[] wrappedBlob) { + this.wrappedBlob = wrappedBlob != null ? Arrays.copyOf(wrappedBlob, wrappedBlob.length) : null; + } + + 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; + } + + @Override + public String toString() { + return String.format("KMSWrappedKey %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "kmsKeyId", "kekVersionId", "zoneId", "created", "removed")); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/HSMProfileDao.java b/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/HSMProfileDao.java new file mode 100644 index 000000000000..a2202a18bfc1 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/HSMProfileDao.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.kms.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.kms.HSMProfileVO; + +public interface HSMProfileDao extends GenericDao { +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/HSMProfileDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/HSMProfileDaoImpl.java new file mode 100644 index 000000000000..b063d2cfe749 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/HSMProfileDaoImpl.java @@ -0,0 +1,29 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.kms.dao; + +import com.cloud.utils.db.GenericDaoBase; +import org.apache.cloudstack.kms.HSMProfileVO; +import org.springframework.stereotype.Component; + +@Component +public class HSMProfileDaoImpl extends GenericDaoBase implements HSMProfileDao { + public HSMProfileDaoImpl() { + super(); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/HSMProfileDetailsDao.java b/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/HSMProfileDetailsDao.java new file mode 100644 index 000000000000..0d5c71b9e5f0 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/HSMProfileDetailsDao.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.kms.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.kms.HSMProfileDetailsVO; + +import java.util.List; + +public interface HSMProfileDetailsDao extends GenericDao { + List listByProfileId(long profileId); + + void persist(long profileId, String name, String value); + + HSMProfileDetailsVO findDetail(long profileId, String name); + + void deleteDetails(long profileId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/HSMProfileDetailsDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/HSMProfileDetailsDaoImpl.java new file mode 100644 index 000000000000..59c0ec43259c --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/HSMProfileDetailsDaoImpl.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.kms.dao; + +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria.Op; +import org.apache.cloudstack.kms.HSMProfileDetailsVO; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +public class HSMProfileDetailsDaoImpl extends GenericDaoBase implements HSMProfileDetailsDao { + + protected SearchBuilder ProfileSearch; + protected SearchBuilder DetailSearch; + + public HSMProfileDetailsDaoImpl() { + super(); + + ProfileSearch = createSearchBuilder(); + ProfileSearch.and("profileId", ProfileSearch.entity().getResourceId(), Op.EQ); + ProfileSearch.done(); + + DetailSearch = createSearchBuilder(); + DetailSearch.and("profileId", DetailSearch.entity().getResourceId(), Op.EQ); + DetailSearch.and("name", DetailSearch.entity().getName(), Op.EQ); + DetailSearch.done(); + } + + @Override + public List listByProfileId(long profileId) { + SearchCriteria sc = ProfileSearch.create(); + sc.setParameters("profileId", profileId); + return listBy(sc); + } + + @Override + public void persist(long profileId, String name, String value) { + HSMProfileDetailsVO vo = new HSMProfileDetailsVO(profileId, name, value); + persist(vo); + } + + @Override + public HSMProfileDetailsVO findDetail(long profileId, String name) { + SearchCriteria sc = DetailSearch.create(); + sc.setParameters("profileId", profileId); + sc.setParameters("name", name); + return findOneBy(sc); + } + + @Override + public void deleteDetails(long profileId) { + SearchCriteria sc = ProfileSearch.create(); + sc.setParameters("profileId", profileId); + remove(sc); + } +} diff --git a/server/src/test/java/org/apache/cloudstack/storage/heuristics/presetvariables/GenericHeuristicPresetVariableTest.java b/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/KMSKekVersionDao.java similarity index 51% rename from server/src/test/java/org/apache/cloudstack/storage/heuristics/presetvariables/GenericHeuristicPresetVariableTest.java rename to engine/schema/src/main/java/org/apache/cloudstack/kms/dao/KMSKekVersionDao.java index cd295e92caf3..dbe4ca5f648a 100644 --- a/server/src/test/java/org/apache/cloudstack/storage/heuristics/presetvariables/GenericHeuristicPresetVariableTest.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/KMSKekVersionDao.java @@ -15,26 +15,29 @@ // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.storage.heuristics.presetvariables; +package org.apache.cloudstack.kms.dao; -import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.kms.KMSKekVersionVO; -@RunWith(MockitoJUnitRunner.class) -public class GenericHeuristicPresetVariableTest { +import java.util.List; - @Test - public void toStringTestReturnsValidJson() { - GenericHeuristicPresetVariable variable = new GenericHeuristicPresetVariable(); - variable.setName("test name"); +public interface KMSKekVersionDao extends GenericDao { - String expected = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(variable, "name"); - String result = variable.toString(); + KMSKekVersionVO getActiveVersion(Long kmsKeyId); - Assert.assertEquals(expected, result); - } + /** + * Returns Active and Previous versions (usable for decryption) + */ + List getVersionsForDecryption(Long kmsKeyId); + List listByKmsKeyId(Long kmsKeyId); + + KMSKekVersionVO findByKmsKeyIdAndVersion(Long kmsKeyId, Integer versionNumber); + + KMSKekVersionVO findByKekLabel(String kekLabel); + + List findByStatus(KMSKekVersionVO.Status status); + + List listByHsmProfileId(Long hsmProfileId); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/KMSKekVersionDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/KMSKekVersionDaoImpl.java new file mode 100644 index 000000000000..841ab8c03d9b --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/KMSKekVersionDaoImpl.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.kms.dao; + +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.apache.cloudstack.kms.KMSKekVersionVO; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +@Component +public class KMSKekVersionDaoImpl extends GenericDaoBase implements KMSKekVersionDao { + + private final SearchBuilder allFieldSearch; + + public KMSKekVersionDaoImpl() { + allFieldSearch = createSearchBuilder(); + allFieldSearch.and("kmsKeyId", allFieldSearch.entity().getKmsKeyId(), SearchCriteria.Op.EQ); + allFieldSearch.and("status", allFieldSearch.entity().getStatus(), SearchCriteria.Op.IN); + allFieldSearch.and("versionNumber", allFieldSearch.entity().getVersionNumber(), SearchCriteria.Op.EQ); + allFieldSearch.and("kekLabel", allFieldSearch.entity().getKekLabel(), SearchCriteria.Op.EQ); + allFieldSearch.and("hsmProfileId", allFieldSearch.entity().getHsmProfileId(), SearchCriteria.Op.EQ); + allFieldSearch.done(); + } + + @Override + public KMSKekVersionVO getActiveVersion(Long kmsKeyId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("kmsKeyId", kmsKeyId); + sc.setParameters("status", KMSKekVersionVO.Status.Active); + return findOneBy(sc); + } + + @Override + public List getVersionsForDecryption(Long kmsKeyId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("kmsKeyId", kmsKeyId); + sc.setParameters("status", KMSKekVersionVO.Status.Active, KMSKekVersionVO.Status.Previous); + return listBy(sc); + } + + @Override + public List listByKmsKeyId(Long kmsKeyId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("kmsKeyId", kmsKeyId); + return listBy(sc); + } + + @Override + public KMSKekVersionVO findByKmsKeyIdAndVersion(Long kmsKeyId, Integer versionNumber) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("kmsKeyId", kmsKeyId); + sc.setParameters("versionNumber", versionNumber); + return findOneBy(sc); + } + + @Override + public KMSKekVersionVO findByKekLabel(String kekLabel) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("kekLabel", kekLabel); + return findOneBy(sc); + } + + @Override + public List findByStatus(KMSKekVersionVO.Status status) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("status", status); + return listBy(sc); + } + + @Override + public List listByHsmProfileId(Long hsmProfileId) { + if (hsmProfileId == null) { + return new ArrayList<>(); + } + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("hsmProfileId", hsmProfileId); + return listBy(sc); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/KMSKeyDao.java b/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/KMSKeyDao.java new file mode 100644 index 000000000000..4a03f2ff96c2 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/KMSKeyDao.java @@ -0,0 +1,35 @@ +// 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.kms.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.framework.kms.KeyPurpose; +import org.apache.cloudstack.kms.KMSKeyVO; + +import java.util.List; + +public interface KMSKeyDao extends GenericDao { + + List listByAccount(Long accountId, KeyPurpose purpose, Boolean enabled); + + List listByZone(Long zoneId, KeyPurpose purpose, Boolean enabled); + + long countByHsmProfileId(Long hsmProfileId); + + KMSKeyVO findByNameAndAccountId(String name, long accountId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/KMSKeyDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/KMSKeyDaoImpl.java new file mode 100644 index 000000000000..ac442c19d1ab --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/KMSKeyDaoImpl.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.kms.dao; + +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.apache.cloudstack.framework.kms.KeyPurpose; +import org.apache.cloudstack.kms.KMSKeyVO; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +public class KMSKeyDaoImpl extends GenericDaoBase implements KMSKeyDao { + + private final SearchBuilder allFieldSearch; + + public KMSKeyDaoImpl() { + allFieldSearch = createSearchBuilder(); + allFieldSearch.and("name", allFieldSearch.entity().getName(), SearchCriteria.Op.EQ); + allFieldSearch.and("kekLabel", allFieldSearch.entity().getKekLabel(), SearchCriteria.Op.EQ); + allFieldSearch.and("domainId", allFieldSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + allFieldSearch.and("accountId", allFieldSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + allFieldSearch.and("purpose", allFieldSearch.entity().getPurpose(), SearchCriteria.Op.EQ); + allFieldSearch.and("enabled", allFieldSearch.entity().isEnabled(), SearchCriteria.Op.EQ); + allFieldSearch.and("zoneId", allFieldSearch.entity().getZoneId(), SearchCriteria.Op.EQ); + allFieldSearch.and("hsmProfileId", allFieldSearch.entity().getHsmProfileId(), SearchCriteria.Op.EQ); + allFieldSearch.done(); + } + + @Override + public List listByAccount(Long accountId, KeyPurpose purpose, Boolean enabled) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("accountId", accountId); + sc.setParametersIfNotNull("purpose", purpose); + sc.setParametersIfNotNull("enabled", enabled); + return listBy(sc); + } + + @Override + public List listByZone(Long zoneId, KeyPurpose purpose, Boolean enabled) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("zoneId", zoneId); + sc.setParametersIfNotNull("purpose", purpose); + sc.setParametersIfNotNull("enabled", enabled); + return listBy(sc); + } + + @Override + public long countByHsmProfileId(Long hsmProfileId) { + if (hsmProfileId == null) { + return 0; + } + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("hsmProfileId", hsmProfileId); + Integer count = getCount(sc); + return count != null ? count : 0; + } + + @Override + public KMSKeyVO findByNameAndAccountId(String name, long accountId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("name", name); + sc.setParameters("accountId", accountId); + return findOneBy(sc); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/KMSWrappedKeyDao.java b/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/KMSWrappedKeyDao.java new file mode 100644 index 000000000000..5e9e418a1667 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/KMSWrappedKeyDao.java @@ -0,0 +1,35 @@ +// 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.kms.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.kms.KMSWrappedKeyVO; + +import java.util.List; + +public interface KMSWrappedKeyDao extends GenericDao { + + long countByKmsKeyId(Long kmsKeyId); + + /** + * Limited variant for batch processing during key rotation + */ + List listByKekVersionId(Long kekVersionId, int limit); + + long countByKekVersionId(Long kekVersionId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/KMSWrappedKeyDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/KMSWrappedKeyDaoImpl.java new file mode 100644 index 000000000000..fc1a7190ae8e --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/kms/dao/KMSWrappedKeyDaoImpl.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 org.apache.cloudstack.kms.dao; + +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.kms.KMSWrappedKeyVO; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +public class KMSWrappedKeyDaoImpl extends GenericDaoBase implements KMSWrappedKeyDao { + + private final SearchBuilder allFieldSearch; + + public KMSWrappedKeyDaoImpl() { + super(); + + allFieldSearch = createSearchBuilder(); + allFieldSearch.and("kmsKeyId", allFieldSearch.entity().getKmsKeyId(), SearchCriteria.Op.EQ); + allFieldSearch.and("kekVersionId", allFieldSearch.entity().getKekVersionId(), SearchCriteria.Op.EQ); + allFieldSearch.and("zoneId", allFieldSearch.entity().getZoneId(), SearchCriteria.Op.EQ); + allFieldSearch.done(); + } + + @Override + public long countByKmsKeyId(Long kmsKeyId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("kmsKeyId", kmsKeyId); + Integer count = getCount(sc); + return count != null ? count.longValue() : 0L; + } + + @Override + public List listByKekVersionId(Long kekVersionId, int limit) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("kekVersionId", kekVersionId); + Filter filter = new Filter(limit); + return listBy(sc, filter); + } + + @Override + public long countByKekVersionId(Long kekVersionId) { + if (kekVersionId == null) { + return 0; + } + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("kekVersionId", kekVersionId); + Integer count = getCount(sc); + return count != null ? count.longValue() : 0L; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/region/gslb/GlobalLoadBalancerRuleVO.java b/engine/schema/src/main/java/org/apache/cloudstack/region/gslb/GlobalLoadBalancerRuleVO.java index 4ce7033156fa..47e2d130d871 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/region/gslb/GlobalLoadBalancerRuleVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/region/gslb/GlobalLoadBalancerRuleVO.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.region.gslb; +import java.util.Date; import java.util.UUID; import javax.persistence.Column; @@ -27,8 +28,11 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; import com.cloud.region.ha.GlobalLoadBalancerRule; +import com.cloud.utils.db.GenericDao; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @@ -74,6 +78,10 @@ public class GlobalLoadBalancerRuleVO implements GlobalLoadBalancerRule { @Column(name = "state") GlobalLoadBalancerRule.State state; + @Column(name = GenericDao.REMOVED_COLUMN) + @Temporal(value = TemporalType.TIMESTAMP) + private Date removed; + public GlobalLoadBalancerRuleVO() { uuid = UUID.randomUUID().toString(); } 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 1102de16e4ea..4383278d7c48 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 @@ -113,4 +113,6 @@ public interface ResourceDetailsDao extends GenericDao long batchExpungeForResources(List ids, Long batchSize); String getActualValue(ResourceDetail resourceDetail); + + List listDetailsForResourceIdsAndKey(List resourceIds, String key); } 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 eafaed182abd..8f376a71f66f 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 @@ -16,11 +16,13 @@ // under the License. package org.apache.cloudstack.resourcedetail; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import com.cloud.utils.StringUtils; import org.apache.commons.collections.CollectionUtils; import com.cloud.utils.Pair; @@ -48,6 +50,7 @@ public abstract class ResourceDetailsDaoBase extends G public ResourceDetailsDaoBase() { AllFieldsSearch = createSearchBuilder(); AllFieldsSearch.and("resourceId", AllFieldsSearch.entity().getResourceId(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("resourceIdIn", AllFieldsSearch.entity().getResourceId(), SearchCriteria.Op.IN); AllFieldsSearch.and("name", AllFieldsSearch.entity().getName(), SearchCriteria.Op.EQ); AllFieldsSearch.and("value", AllFieldsSearch.entity().getValue(), SearchCriteria.Op.EQ); // FIXME SnapshotDetailsVO doesn't have a display field @@ -266,4 +269,15 @@ public String getActualValue(ResourceDetail resourceDetail) { } return resourceDetail.getValue(); } + + @Override + public List listDetailsForResourceIdsAndKey(List resourceIds, String key) { + if (CollectionUtils.isEmpty(resourceIds) || StringUtils.isBlank(key)) { + return Collections.emptyList(); + } + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("name", key); + sc.setParameters("resourceIdIn", resourceIds.toArray()); + return search(sc, null); + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/UserDetailVO.java b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/UserDetailVO.java index d0cfcc3d4396..93b49bc20a10 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/UserDetailVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/UserDetailVO.java @@ -48,6 +48,8 @@ public class UserDetailVO implements ResourceDetail { public static final String Setup2FADetail = "2FASetupStatus"; public static final String PasswordResetToken = "PasswordResetToken"; public static final String PasswordResetTokenExpiryDate = "PasswordResetTokenExpiryDate"; + public static final String PasswordChangeRequired = "PasswordChangeRequired"; + public static final String OauthLogin = "OauthLogin"; public UserDetailVO() { } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/schedule/ResourceScheduleDetailVO.java b/engine/schema/src/main/java/org/apache/cloudstack/schedule/ResourceScheduleDetailVO.java new file mode 100644 index 000000000000..16985e6bac30 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/schedule/ResourceScheduleDetailVO.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.schedule; + +import org.apache.cloudstack.api.ResourceDetail; + +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 = "resource_schedule_details") +public class ResourceScheduleDetailVO implements ResourceDetail { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "schedule_id") + private long resourceId; + + @Column(name = "name") + private String name; + + @Column(name = "value", length = 1024) + private String value; + + @Column(name = "display") + private boolean display = true; + + public ResourceScheduleDetailVO() { + } + + public ResourceScheduleDetailVO(long scheduleId, String name, String value, boolean display) { + this.resourceId = scheduleId; + 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; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduleVO.java b/engine/schema/src/main/java/org/apache/cloudstack/schedule/ResourceScheduleVO.java similarity index 71% rename from engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduleVO.java rename to engine/schema/src/main/java/org/apache/cloudstack/schedule/ResourceScheduleVO.java index e0065db1e77a..33a09d79239d 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduleVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/schedule/ResourceScheduleVO.java @@ -16,9 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.cloudstack.vm.schedule; +package org.apache.cloudstack.schedule; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import javax.persistence.Column; @@ -37,8 +38,8 @@ import java.util.UUID; @Entity -@Table(name = "vm_schedule") -public class VMScheduleVO implements VMSchedule { +@Table(name = "resource_schedule") +public class ResourceScheduleVO implements ResourceSchedule { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) @@ -50,8 +51,12 @@ public class VMScheduleVO implements VMSchedule { @Column(name = "description") String description; - @Column(name = "vm_id", nullable = false) - long vmId; + @Enumerated(value = EnumType.STRING) + @Column(name = "resource_type", nullable = false) + ApiCommandResourceType resourceType; + + @Column(name = "resource_id", nullable = false) + long resourceId; @Column(name = "schedule", nullable = false) String schedule; @@ -60,8 +65,7 @@ public class VMScheduleVO implements VMSchedule { String timeZone; @Column(name = "action", nullable = false) - @Enumerated(value = EnumType.STRING) - Action action; + String actionName; @Column(name = "enabled", nullable = false) boolean enabled; @@ -80,17 +84,19 @@ public class VMScheduleVO implements VMSchedule { @Column(name = GenericDao.REMOVED_COLUMN) Date removed; - public VMScheduleVO() { + public ResourceScheduleVO() { uuid = UUID.randomUUID().toString(); } - public VMScheduleVO(long vmId, String description, String schedule, String timeZone, Action action, Date startDate, Date endDate, boolean enabled) { + public ResourceScheduleVO(ApiCommandResourceType resourceType, long resourceId, String description, String schedule, + String timeZone, String action, Date startDate, Date endDate, boolean enabled) { uuid = UUID.randomUUID().toString(); - this.vmId = vmId; + this.resourceType = resourceType; + this.resourceId = resourceId; this.description = description; this.schedule = schedule; this.timeZone = timeZone; - this.action = action; + this.actionName = action; this.startDate = startDate; this.endDate = endDate; this.enabled = enabled; @@ -98,7 +104,8 @@ public VMScheduleVO(long vmId, String description, String schedule, String timeZ @Override public String toString() { - return String.format("VMSchedule %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "uuid", "action", "description")); + return String.format("ResourceSchedule %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "resourceType", "actionName", "description", "enabled")); } @Override @@ -111,14 +118,25 @@ public long getId() { return id; } - public long getVmId() { - return vmId; + @Override + public ApiCommandResourceType getResourceType() { + return resourceType; + } + + public void setResourceType(ApiCommandResourceType resourceType) { + this.resourceType = resourceType; + } + + @Override + public long getResourceId() { + return resourceId; } - public void setVmId(long vmId) { - this.vmId = vmId; + public void setResourceId(long resourceId) { + this.resourceId = resourceId; } + @Override public String getDescription() { return description; } @@ -127,6 +145,7 @@ public void setDescription(String description) { this.description = description; } + @Override public String getSchedule() { return schedule.substring(2); } @@ -144,14 +163,16 @@ public void setTimeZone(String timeZone) { this.timeZone = timeZone; } - public Action getAction() { - return action; + @Override + public String getActionName() { + return actionName; } - public void setAction(Action action) { - this.action = action; + public void setActionName(String action) { + this.actionName = action; } + @Override public boolean getEnabled() { return enabled; } @@ -183,6 +204,7 @@ public ZoneId getTimeZoneId() { return TimeZone.getTimeZone(getTimeZone()).toZoneId(); } + @Override public Date getCreated() { return created; } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduledJobVO.java b/engine/schema/src/main/java/org/apache/cloudstack/schedule/ResourceScheduledJobVO.java similarity index 64% rename from engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduledJobVO.java rename to engine/schema/src/main/java/org/apache/cloudstack/schedule/ResourceScheduledJobVO.java index 775e9cfe40cf..ca03056b67a8 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduledJobVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/schedule/ResourceScheduledJobVO.java @@ -16,8 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.cloudstack.vm.schedule; +package org.apache.cloudstack.schedule; +import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import javax.persistence.Column; @@ -34,8 +35,8 @@ import java.util.UUID; @Entity -@Table(name = "vm_scheduled_job") -public class VMScheduledJobVO implements VMScheduledJob { +@Table(name = "resource_scheduled_job") +public class ResourceScheduledJobVO implements ResourceScheduledJob { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") @@ -44,41 +45,44 @@ public class VMScheduledJobVO implements VMScheduledJob { @Column(name = "uuid", nullable = false) String uuid; - @Column(name = "vm_id", nullable = false) - long vmId; + @Enumerated(value = EnumType.STRING) + @Column(name = "resource_type", nullable = false) + ApiCommandResourceType resourceType; + + @Column(name = "resource_id", nullable = false) + long resourceId; - @Column(name = "vm_schedule_id", nullable = false) - long vmScheduleId; + @Column(name = "schedule_id", nullable = false) + long scheduleId; @Column(name = "async_job_id") Long asyncJobId; @Column(name = "action", nullable = false) - @Enumerated(value = EnumType.STRING) - VMSchedule.Action action; + String actionName; @Column(name = "scheduled_timestamp") @Temporal(value = TemporalType.TIMESTAMP) Date scheduledTime; - public VMScheduledJobVO() { + public ResourceScheduledJobVO() { uuid = UUID.randomUUID().toString(); } - public VMScheduledJobVO(long vmId, long vmScheduleId, VMSchedule.Action action, Date scheduledTime) { + public ResourceScheduledJobVO(ApiCommandResourceType resourceType, long resourceId, long scheduleId, String action, Date scheduledTime) { uuid = UUID.randomUUID().toString(); - this.vmId = vmId; - this.vmScheduleId = vmScheduleId; - this.action = action; + this.resourceType = resourceType; + this.resourceId = resourceId; + this.scheduleId = scheduleId; + this.actionName = action; this.scheduledTime = scheduledTime; } - @Override public String toString() { - return String.format("VMScheduledJob %s", + return String.format("ResourceScheduledJob %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( - this, "id", "uuid", "action", "vmScheduleId", "vmId", "asyncJobId")); + this, "id", "uuid", "resourceType", "actionName", "scheduleId", "resourceId", "asyncJobId")); } @Override @@ -92,13 +96,18 @@ public long getId() { } @Override - public long getVmId() { - return vmId; + public ApiCommandResourceType getResourceType() { + return resourceType; } @Override - public long getVmScheduleId() { - return vmScheduleId; + public long getResourceId() { + return resourceId; + } + + @Override + public long getScheduleId() { + return scheduleId; } @Override @@ -112,8 +121,12 @@ public void setAsyncJobId(long asyncJobId) { } @Override - public VMSchedule.Action getAction() { - return action; + public String getActionName() { + return actionName; + } + + public void setActionName(String action) { + this.actionName = action; } @Override diff --git a/engine/schema/src/main/java/org/apache/cloudstack/schedule/dao/ResourceScheduleDao.java b/engine/schema/src/main/java/org/apache/cloudstack/schedule/dao/ResourceScheduleDao.java new file mode 100644 index 000000000000..ee56991fb9ac --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/schedule/dao/ResourceScheduleDao.java @@ -0,0 +1,40 @@ +/* + * 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.schedule.dao; + +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; +import com.cloud.utils.db.SearchCriteria; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.schedule.ResourceScheduleVO; + +import java.util.List; + +public interface ResourceScheduleDao extends GenericDao { + List listAllActiveSchedules(ApiCommandResourceType resourceType); + + long removeSchedulesForResourceAndIds(ApiCommandResourceType resourceType, long resourceId, List ids); + + long removeAllSchedulesForResource(ApiCommandResourceType resourceType, long resourceId); + + Pair, Integer> searchAndCount(List ids, ApiCommandResourceType resourceType, Long resourceId, + String action, Boolean enabled, Long offset, Long limit); + + SearchCriteria getSearchCriteriaForResource(ApiCommandResourceType resourceType, long resourceId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/schedule/dao/ResourceScheduleDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/schedule/dao/ResourceScheduleDaoImpl.java new file mode 100644 index 000000000000..7217d1359a81 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/schedule/dao/ResourceScheduleDaoImpl.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.schedule.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.ApiCommandResourceType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.schedule.ResourceScheduleVO; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +public class ResourceScheduleDaoImpl extends GenericDaoBase implements ResourceScheduleDao { + + private final SearchBuilder activeScheduleSearch; + private final SearchBuilder allSearch; + + static final String RESOURCE_TYPE = "resourceType"; + static final String RESOURCE_ID = "resourceId"; + + public ResourceScheduleDaoImpl() { + super(); + + activeScheduleSearch = createSearchBuilder(); + activeScheduleSearch.and(RESOURCE_TYPE, activeScheduleSearch.entity().getResourceType(), SearchCriteria.Op.EQ); + activeScheduleSearch.and(ApiConstants.ENABLED, activeScheduleSearch.entity().getEnabled(), SearchCriteria.Op.EQ); + activeScheduleSearch.done(); + + allSearch = createSearchBuilder(); + allSearch.and(ApiConstants.ID, allSearch.entity().getId(), SearchCriteria.Op.IN); + allSearch.and(RESOURCE_TYPE, allSearch.entity().getResourceType(), SearchCriteria.Op.EQ); + allSearch.and(RESOURCE_ID, allSearch.entity().getResourceId(), SearchCriteria.Op.EQ); + allSearch.and(ApiConstants.ACTION, allSearch.entity().getActionName(), SearchCriteria.Op.EQ); + allSearch.and(ApiConstants.ENABLED, allSearch.entity().getEnabled(), SearchCriteria.Op.EQ); + allSearch.done(); + } + + @Override + public List listAllActiveSchedules(ApiCommandResourceType resourceType) { + SearchCriteria sc = activeScheduleSearch.create(); + sc.setParameters(RESOURCE_TYPE, resourceType); + sc.setParameters(ApiConstants.ENABLED, true); + return search(sc, null); + } + + @Override + public long removeSchedulesForResourceAndIds(ApiCommandResourceType resourceType, long resourceId, List ids) { + SearchCriteria sc = allSearch.create(); + if (CollectionUtils.isNotEmpty(ids)) { + sc.setParameters(ApiConstants.ID, ids.toArray()); + } + sc.setParameters(RESOURCE_TYPE, resourceType); + sc.setParameters(RESOURCE_ID, resourceId); + return remove(sc); + } + + @Override + public long removeAllSchedulesForResource(ApiCommandResourceType resourceType, long resourceId) { + SearchCriteria sc = allSearch.create(); + sc.setParameters(RESOURCE_TYPE, resourceType); + sc.setParameters(RESOURCE_ID, resourceId); + return remove(sc); + } + + @Override + public Pair, Integer> searchAndCount(List ids, ApiCommandResourceType resourceType, Long resourceId, + String action, Boolean enabled, Long offset, Long limit) { + SearchCriteria sc = allSearch.create(); + if (CollectionUtils.isNotEmpty(ids)) { + sc.setParameters(ApiConstants.ID, ids.toArray()); + } + sc.setParametersIfNotNull(ApiConstants.ENABLED, enabled); + sc.setParametersIfNotNull(ApiConstants.ACTION, action); + sc.setParametersIfNotNull(RESOURCE_TYPE, resourceType); + sc.setParametersIfNotNull(RESOURCE_ID, resourceId); + Filter filter = new Filter(ResourceScheduleVO.class, ApiConstants.ID, false, offset, limit); + return searchAndCount(sc, filter); + } + + @Override + public SearchCriteria getSearchCriteriaForResource(ApiCommandResourceType resourceType, long resourceId) { + SearchCriteria sc = allSearch.create(); + sc.setParameters(RESOURCE_TYPE, resourceType); + sc.setParameters(RESOURCE_ID, resourceId); + return sc; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/schedule/dao/ResourceScheduleDetailsDao.java b/engine/schema/src/main/java/org/apache/cloudstack/schedule/dao/ResourceScheduleDetailsDao.java new file mode 100644 index 000000000000..f36ce8d0879c --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/schedule/dao/ResourceScheduleDetailsDao.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.schedule.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; +import org.apache.cloudstack.schedule.ResourceScheduleDetailVO; + +public interface ResourceScheduleDetailsDao extends GenericDao, ResourceDetailsDao { +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/schedule/dao/ResourceScheduleDetailsDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/schedule/dao/ResourceScheduleDetailsDaoImpl.java new file mode 100644 index 000000000000..3757e41d0cfe --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/schedule/dao/ResourceScheduleDetailsDaoImpl.java @@ -0,0 +1,28 @@ +// 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.schedule.dao; + +import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; +import org.apache.cloudstack.schedule.ResourceScheduleDetailVO; + +public class ResourceScheduleDetailsDaoImpl extends ResourceDetailsDaoBase implements ResourceScheduleDetailsDao { + + @Override + public void addDetail(long resourceId, String key, String value, boolean display) { + super.addDetail(new ResourceScheduleDetailVO(resourceId, key, value, display)); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduleDao.java b/engine/schema/src/main/java/org/apache/cloudstack/schedule/dao/ResourceScheduledJobDao.java similarity index 57% rename from engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduleDao.java rename to engine/schema/src/main/java/org/apache/cloudstack/schedule/dao/ResourceScheduledJobDao.java index b8c808b5bfc9..16210802235f 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduleDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/schedule/dao/ResourceScheduledJobDao.java @@ -16,22 +16,21 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.cloudstack.vm.schedule.dao; +package org.apache.cloudstack.schedule.dao; -import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDao; -import com.cloud.utils.db.SearchCriteria; -import org.apache.cloudstack.vm.schedule.VMSchedule; -import org.apache.cloudstack.vm.schedule.VMScheduleVO; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.schedule.ResourceScheduledJobVO; +import java.util.Date; import java.util.List; -public interface VMScheduleDao extends GenericDao { - List listAllActiveSchedules(); +public interface ResourceScheduledJobDao extends GenericDao { + List listJobsToStart(ApiCommandResourceType resourceType, Date currentTimestamp); - long removeSchedulesForVmIdAndIds(Long vmId, List ids); + int expungeJobsForSchedules(List scheduleIds, Date dateAfter); - Pair, Integer> searchAndCount(Long id, Long vmId, VMSchedule.Action action, Boolean enabled, Long offset, Long limit); + int expungeJobsBefore(ApiCommandResourceType resourceType, Date currentTimestamp); - SearchCriteria getSearchCriteriaForVMId(Long vmId); + ResourceScheduledJobVO findByScheduleAndTimestamp(long scheduleId, Date scheduledTimestamp); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/schedule/dao/ResourceScheduledJobDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/schedule/dao/ResourceScheduledJobDaoImpl.java new file mode 100644 index 000000000000..02c1acb6c719 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/schedule/dao/ResourceScheduledJobDaoImpl.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.schedule.dao; + +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.ApiCommandResourceType; +import org.apache.cloudstack.schedule.ResourceScheduledJobVO; +import org.apache.commons.lang3.time.DateUtils; +import org.springframework.stereotype.Component; + +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +@Component +public class ResourceScheduledJobDaoImpl extends GenericDaoBase implements ResourceScheduledJobDao { + + private final SearchBuilder jobsToStartSearch; + private final SearchBuilder expungeJobsBeforeSearch; + private final SearchBuilder expungeJobForScheduleSearch; + private final SearchBuilder scheduleAndTimestampSearch; + + static final String SCHEDULED_TIMESTAMP = "scheduledTimestamp"; + static final String SCHEDULE_ID = "scheduleId"; + static final String RESOURCE_TYPE = "resourceType"; + + public ResourceScheduledJobDaoImpl() { + super(); + + jobsToStartSearch = createSearchBuilder(); + jobsToStartSearch.and(RESOURCE_TYPE, jobsToStartSearch.entity().getResourceType(), SearchCriteria.Op.EQ); + jobsToStartSearch.and(SCHEDULED_TIMESTAMP, jobsToStartSearch.entity().getScheduledTime(), SearchCriteria.Op.EQ); + jobsToStartSearch.and("async_job_id", jobsToStartSearch.entity().getAsyncJobId(), SearchCriteria.Op.NULL); + jobsToStartSearch.done(); + + expungeJobsBeforeSearch = createSearchBuilder(); + expungeJobsBeforeSearch.and(RESOURCE_TYPE, expungeJobsBeforeSearch.entity().getResourceType(), SearchCriteria.Op.EQ); + expungeJobsBeforeSearch.and(SCHEDULED_TIMESTAMP, expungeJobsBeforeSearch.entity().getScheduledTime(), SearchCriteria.Op.LT); + expungeJobsBeforeSearch.done(); + + expungeJobForScheduleSearch = createSearchBuilder(); + expungeJobForScheduleSearch.and(SCHEDULE_ID, expungeJobForScheduleSearch.entity().getScheduleId(), SearchCriteria.Op.IN); + expungeJobForScheduleSearch.and(SCHEDULED_TIMESTAMP, expungeJobForScheduleSearch.entity().getScheduledTime(), SearchCriteria.Op.GTEQ); + expungeJobForScheduleSearch.done(); + + scheduleAndTimestampSearch = createSearchBuilder(); + scheduleAndTimestampSearch.and(SCHEDULE_ID, scheduleAndTimestampSearch.entity().getScheduleId(), SearchCriteria.Op.EQ); + scheduleAndTimestampSearch.and(SCHEDULED_TIMESTAMP, scheduleAndTimestampSearch.entity().getScheduledTime(), SearchCriteria.Op.EQ); + scheduleAndTimestampSearch.done(); + } + + @Override + public List listJobsToStart(ApiCommandResourceType resourceType, Date currentTimestamp) { + if (currentTimestamp == null) { + currentTimestamp = new Date(); + } + Date truncatedTs = DateUtils.round(currentTimestamp, Calendar.MINUTE); + SearchCriteria sc = jobsToStartSearch.create(); + sc.setParameters(RESOURCE_TYPE, resourceType); + sc.setParameters(SCHEDULED_TIMESTAMP, truncatedTs); + Filter filter = new Filter(ResourceScheduledJobVO.class, "scheduleId", true, null, null); + return search(sc, filter); + } + + @Override + public int expungeJobsForSchedules(List scheduleIds, Date dateAfter) { + SearchCriteria sc = expungeJobForScheduleSearch.create(); + sc.setParameters(SCHEDULE_ID, scheduleIds.toArray()); + if (dateAfter != null) { + sc.setParameters(SCHEDULED_TIMESTAMP, dateAfter); + } + return expunge(sc); + } + + @Override + public int expungeJobsBefore(ApiCommandResourceType resourceType, Date date) { + SearchCriteria sc = expungeJobsBeforeSearch.create(); + sc.setParameters(RESOURCE_TYPE, resourceType); + sc.setParameters(SCHEDULED_TIMESTAMP, date); + return expunge(sc); + } + + @Override + public ResourceScheduledJobVO findByScheduleAndTimestamp(long scheduleId, Date scheduledTimestamp) { + SearchCriteria sc = scheduleAndTimestampSearch.create(); + sc.setParameters(SCHEDULE_ID, scheduleId); + sc.setParameters(SCHEDULED_TIMESTAMP, scheduledTimestamp); + return findOneBy(sc); + } +} 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 4cb40b5eaf63..5f32a3502328 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 @@ -202,7 +202,7 @@ public ImageStoreVO findOneByZoneAndProtocol(long dataCenterId, String protocol) sc.setParameters("dataCenterId", dataCenterId); sc.setParameters("protocol", protocol); sc.setParameters("role", DataStoreRole.Image); - Filter filter = new Filter(1); + Filter filter = new Filter(1, true); List results = listBy(sc, filter); return results.size() == 0 ? null : results.get(0); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDetailsDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDetailsDaoImpl.java index ec40dc0dd683..d7e88bd31c3f 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDetailsDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDetailsDaoImpl.java @@ -77,7 +77,7 @@ public Map getDetails(long storeId) { for (ImageStoreDetailVO detail : details) { String name = detail.getName(); String value = detail.getValue(); - if (name.equals(ApiConstants.KEY) || name.equals(ApiConstants.S3_SECRET_KEY)) { + if (name.equals(ApiConstants.KEY) || name.equals(ApiConstants.SECRET_KEY)) { value = DBEncryptionUtil.decrypt(value); } detailsMap.put(name, value); 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 37aa70abb6ed..57b5f6461c6a 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 @@ -171,4 +171,5 @@ Pair, Integer> searchForIdsAndCount(Long storagePoolId, String storag List findPoolsByStorageTypeAndZone(Storage.StoragePoolType storageType, Long zoneId); + List listByDataCenterIds(List dataCenterIds); } 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 8b230d03154e..b5d6415e3a1a 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 @@ -65,6 +65,7 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase private final GenericSearchBuilder StatusCountSearch; private final SearchBuilder ClustersSearch; private final SearchBuilder IdsSearch; + private final SearchBuilder DcsSearch; @Inject private StoragePoolDetailsDao _detailsDao; @@ -167,6 +168,9 @@ public PrimaryDataStoreDaoImpl() { IdsSearch.and("ids", IdsSearch.entity().getId(), SearchCriteria.Op.IN); IdsSearch.done(); + DcsSearch = createSearchBuilder(); + DcsSearch.and("dataCenterId", DcsSearch.entity().getDataCenterId(), SearchCriteria.Op.IN); + DcsSearch.done(); } @Override @@ -320,6 +324,9 @@ public StoragePoolVO persist(StoragePoolVO pool, Map details, Li pool = super.persist(pool); if (details != null) { for (Map.Entry detail : details.entrySet()) { + if (detail.getKey().toLowerCase().contains("password") || detail.getKey().toLowerCase().contains("token")) { + displayDetails = false; + } StoragePoolDetailVO vo = new StoragePoolDetailVO(pool.getId(), detail.getKey(), detail.getValue(), displayDetails); _detailsDao.persist(vo); } @@ -924,6 +931,16 @@ public List findPoolsByStorageTypeAndZone(Storage.StoragePoolType return listBy(sc); } + @Override + public List listByDataCenterIds(List dataCenterIds) { + if (CollectionUtils.isEmpty(dataCenterIds)) { + return Collections.emptyList(); + } + SearchCriteria sc = DcsSearch.create(); + sc.setParameters("dataCenterId", dataCenterIds.toArray()); + return listBy(sc); + } + private SearchCriteria createStoragePoolSearchCriteria(Long storagePoolId, String storagePoolName, Long zoneId, String path, Long podId, Long clusterId, Long hostId, String address, ScopeType scopeType, StoragePoolStatus status, String keyword, String storageAccessGroup) { 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 902cb73dc055..2d337fe07fc7 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 @@ -51,6 +51,8 @@ public interface SnapshotDataStoreDao extends GenericDao listBySnapshotIdAndDataStoreRoleAndStateIn(long snapshotId, DataStoreRole role, ObjectInDataStoreStateMachine.State... state); + List listReadyByVolumeIdAndCheckpointPathNotNull(long volumeId); SnapshotDataStoreVO findOneBySnapshotId(long snapshotId, long zoneId); @@ -76,6 +78,8 @@ public interface SnapshotDataStoreDao extends GenericDao findBySnapshotId(long snapshotId); + List findBySnapshotIdWithNonDestroyedState(long snapshotId); + void duplicateCacheRecordsOnRegionStore(long storeId); // delete the snapshot entry on primary data store to make sure that next snapshot will be full snapshot @@ -95,6 +99,8 @@ public interface SnapshotDataStoreDao extends GenericDao findByVolume(long snapshotId, long volumeId, DataStoreRole role); + void expungeBySnapshotIdAndStoreRole(long snapshotId, DataStoreRole role); + /** * List all snapshots in 'snapshot_store_ref' by volume and data store role. Therefore, it is possible to list all snapshots that are in the primary storage or in the secondary storage. */ @@ -128,4 +134,18 @@ public interface SnapshotDataStoreDao extends GenericDao snapshotIds, Long batchSize); + + /** + * Returns the total physical size, in bytes, of all snapshots stored on primary + * storage for the specified account that have not yet been backed up to + * secondary storage. + * + *

If no such snapshots are found, this method returns {@code 0}.

+ * + * @param accountId the ID of the account whose snapshots on primary storage + * should be considered + * @return the total physical size in bytes of matching snapshots on primary + * storage, or {@code 0} if none are found + */ + long getSnapshotsPhysicalSizeOnPrimaryStorageByAccountId(long accountId); } 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 241c3df2e4a8..cb88e21cf9d6 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 @@ -63,11 +63,12 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq; protected SearchBuilder searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq; private SearchBuilder stateSearch; private SearchBuilder idStateNinSearch; + private SearchBuilder idEqRoleEqStateInSearch; protected SearchBuilder snapshotVOSearch; private SearchBuilder snapshotCreatedSearch; private SearchBuilder dataStoreAndInstallPathSearch; @@ -75,7 +76,9 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase storeSnapshotDownloadStatusSearch; private SearchBuilder searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull; private SearchBuilder searchFilterStateAndDownloadUrlNotNullAndDownloadUrlCreatedBefore; - private SearchBuilder searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEq; + private SearchBuilder searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqPathEq; + + private SearchBuilder searchFilteringVolumeIdAndStateAndCreatedAfter; private SearchBuilder searchBySnapshotId; @@ -96,6 +99,15 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase params) throws ConfigurationException { super.configure(name, params); @@ -142,6 +154,11 @@ public boolean configure(String name, Map params) throws Configu idStateNinSearch.and(STATE, idStateNinSearch.entity().getState(), SearchCriteria.Op.NOTIN); idStateNinSearch.done(); + idEqRoleEqStateInSearch = createSearchBuilder(); + idEqRoleEqStateInSearch.and(SNAPSHOT_ID, idEqRoleEqStateInSearch.entity().getSnapshotId(), SearchCriteria.Op.EQ); + idEqRoleEqStateInSearch.and(STORE_ROLE, idEqRoleEqStateInSearch.entity().getRole(), SearchCriteria.Op.EQ); + idEqRoleEqStateInSearch.and(STATE, idEqRoleEqStateInSearch.entity().getState(), SearchCriteria.Op.IN); + snapshotVOSearch = snapshotDao.createSearchBuilder(); snapshotVOSearch.and(VOLUME_ID, snapshotVOSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); snapshotVOSearch.done(); @@ -183,17 +200,24 @@ public boolean configure(String name, Map params) throws Configu searchFilterStateAndDownloadUrlNotNullAndDownloadUrlCreatedBefore.and(URL_CREATED_BEFORE, searchFilterStateAndDownloadUrlNotNullAndDownloadUrlCreatedBefore.entity().getExtractUrlCreated(), SearchCriteria.Op.LT); searchFilterStateAndDownloadUrlNotNullAndDownloadUrlCreatedBefore.done(); - searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEq = createSearchBuilder(); - searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEq.and(STATE, searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEq.entity().getState(), SearchCriteria.Op.EQ); - searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEq.and(VOLUME_ID, searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEq.entity().getVolumeId(), SearchCriteria.Op.EQ); - searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEq.and(STORE_ROLE, searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEq.entity().getRole(), SearchCriteria.Op.EQ); - searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEq.and(STORE_ID, searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEq.entity().getDataStoreId(), SearchCriteria.Op.IN); + searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqPathEq = createSearchBuilder(); + searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqPathEq.and(STATE, searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqPathEq.entity().getState(), SearchCriteria.Op.EQ); + searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqPathEq.and(VOLUME_ID, searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqPathEq.entity().getVolumeId(), SearchCriteria.Op.EQ); + searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqPathEq.and(STORE_ROLE, searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqPathEq.entity().getRole(), SearchCriteria.Op.EQ); + searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqPathEq.and(STORE_ID, searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqPathEq.entity().getDataStoreId(), SearchCriteria.Op.IN); + searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqPathEq.and(INSTALL_PATH, searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqPathEq.entity().getInstallPath(), SearchCriteria.Op.EQ); + searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqPathEq.done(); searchBySnapshotId = createSearchBuilder(); searchBySnapshotId.and(SNAPSHOT_ID, searchBySnapshotId.entity().getSnapshotId(), SearchCriteria.Op.EQ); searchBySnapshotId.and(STATE, searchBySnapshotId.entity().getState(), SearchCriteria.Op.EQ); searchBySnapshotId.done(); + searchFilteringVolumeIdAndStateAndCreatedAfter = createSearchBuilder(); + searchFilteringVolumeIdAndStateAndCreatedAfter.and(STATE, searchFilteringVolumeIdAndStateAndCreatedAfter.entity().getState(), SearchCriteria.Op.EQ); + searchFilteringVolumeIdAndStateAndCreatedAfter.and(VOLUME_ID, searchFilteringVolumeIdAndStateAndCreatedAfter.entity().getVolumeId(), SearchCriteria.Op.EQ); + searchFilteringVolumeIdAndStateAndCreatedAfter.and(CREATED, searchFilteringVolumeIdAndStateAndCreatedAfter.entity().getCreated(), SearchCriteria.Op.GT); + searchFilteringVolumeIdAndStateAndCreatedAfter.done(); return true; } @@ -341,7 +365,7 @@ public SnapshotDataStoreVO findParent(DataStoreRole role, Long storeId, Long zon if (kvmIncrementalSnapshot && Hypervisor.HypervisorType.KVM.equals(hypervisorType)) { sc = searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull.create(); } else { - sc = searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEq.create(); + sc = searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqPathEq.create(); } sc.setParameters(VOLUME_ID, volumeId); @@ -378,6 +402,15 @@ public SnapshotDataStoreVO findBySnapshotIdAndDataStoreRoleAndState(long snapsho return findOneBy(sc); } + @Override + public List listBySnapshotIdAndDataStoreRoleAndStateIn(long snapshotId, DataStoreRole role, State... state) { + SearchCriteria sc = idEqRoleEqStateInSearch.create(); + sc.setParameters(SNAPSHOT_ID, snapshotId); + sc.setParameters(STORE_ROLE, role); + sc.setParameters(STATE, (Object[])state); + return listBy(sc); + } + @Override public SnapshotDataStoreVO findOneBySnapshotId(long snapshotId, long zoneId) { try (TransactionLegacy transactionLegacy = TransactionLegacy.currentTxn()) { @@ -445,6 +478,12 @@ public SnapshotDataStoreVO findBySnapshotIdInAnyState(long snapshotId, DataStore return findOneBy(sc); } + @Override + public void expungeBySnapshotIdAndStoreRole(long snapshotId, DataStoreRole role) { + SearchCriteria sc = createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, role); + expunge(sc); + } + @Override public List listAllByVolumeAndDataStore(long volumeId, DataStoreRole role) { SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); @@ -464,6 +503,13 @@ public List findByVolume(long snapshotId, long volumeId, Da @Override public List findBySnapshotId(long snapshotId) { + SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + sc.setParameters(SNAPSHOT_ID, snapshotId); + return listBy(sc); + } + + @Override + public List findBySnapshotIdWithNonDestroyedState(long snapshotId) { SearchCriteria sc = idStateNinSearch.create(); sc.setParameters(SNAPSHOT_ID, snapshotId); sc.setParameters(STATE, State.Destroyed.name()); @@ -725,4 +771,23 @@ public int expungeBySnapshotList(final List snapshotIds, final Long batchS sc.setParameters("snapshotIds", snapshotIds.toArray()); return batchExpunge(sc, batchSize); } + + @Override + public long getSnapshotsPhysicalSizeOnPrimaryStorageByAccountId(long accountId) { + long snapshotsPhysicalSize = 0; + try (TransactionLegacy transactionLegacy = TransactionLegacy.currentTxn()) { + try (PreparedStatement preparedStatement = transactionLegacy.prepareStatement(GET_PHYSICAL_SIZE_OF_SNAPSHOTS_ON_PRIMARY_BY_ACCOUNT)) { + preparedStatement.setLong(1, accountId); + + try (ResultSet resultSet = preparedStatement.executeQuery()) { + if (resultSet.next()) { + snapshotsPhysicalSize = resultSet.getLong(1); + } + } + } + } catch (SQLException e) { + logger.warn("Failed to get the snapshots physical size for the account [{}] due to [{}].", accountId, e.getMessage(), e); + } + return snapshotsPhysicalSize; + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/VolumeDataStoreVO.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/VolumeDataStoreVO.java index d57dec8fbfd5..c475a4203a73 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/VolumeDataStoreVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/VolumeDataStoreVO.java @@ -209,10 +209,8 @@ public VolumeDataStoreVO(long hostId, long volumeId) { public VolumeDataStoreVO(long hostId, long volumeId, Date lastUpdated, int downloadPercent, Status downloadState, String localDownloadPath, String errorString, String jobId, String installPath, String downloadUrl, String checksum) { - // super(); dataStoreId = hostId; this.volumeId = volumeId; - // this.zoneId = zoneId; this.lastUpdated = lastUpdated; this.downloadPercent = downloadPercent; this.downloadState = downloadState; diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/sharedfs/dao/SharedFSDao.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/sharedfs/dao/SharedFSDao.java index 4735202a7623..82ba9445b25c 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/sharedfs/dao/SharedFSDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/sharedfs/dao/SharedFSDao.java @@ -29,4 +29,6 @@ public interface SharedFSDao extends GenericDao, StateDao listSharedFSToBeDestroyed(Date date); SharedFSVO findSharedFSByNameAccountDomain(String name, Long accountId, Long domainId); + + SharedFSVO findByVm(long vmId); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/sharedfs/dao/SharedFSDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/sharedfs/dao/SharedFSDaoImpl.java index da6220716715..dd23787f982f 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/sharedfs/dao/SharedFSDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/sharedfs/dao/SharedFSDaoImpl.java @@ -114,4 +114,14 @@ public SharedFSVO findSharedFSByNameAccountDomain(String name, Long accountId, L sc.setParameters("domainId", domainId); return findOneBy(sc); } + + @Override + public SharedFSVO findByVm(long vmId) { + SearchBuilder sb = createSearchBuilder(); + sb.and("vmId", sb.entity().getVmId(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("vmId", vmId); + return findOneBy(sc); + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduleDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduleDaoImpl.java deleted file mode 100644 index db8c0c3068f8..000000000000 --- a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduleDaoImpl.java +++ /dev/null @@ -1,108 +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.vm.schedule.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.vm.schedule.VMSchedule; -import org.apache.cloudstack.vm.schedule.VMScheduleVO; -import org.springframework.stereotype.Component; - -import java.util.Date; -import java.util.List; - -@Component -public class VMScheduleDaoImpl extends GenericDaoBase implements VMScheduleDao { - - private final SearchBuilder activeScheduleSearch; - - private final SearchBuilder scheduleSearchByVmIdAndIds; - - private final SearchBuilder scheduleSearch; - - public VMScheduleDaoImpl() { - super(); - activeScheduleSearch = createSearchBuilder(); - activeScheduleSearch.and(ApiConstants.ENABLED, activeScheduleSearch.entity().getEnabled(), SearchCriteria.Op.EQ); - activeScheduleSearch.and().op(activeScheduleSearch.entity().getEndDate(), SearchCriteria.Op.NULL); - activeScheduleSearch.or(ApiConstants.END_DATE, activeScheduleSearch.entity().getEndDate(), SearchCriteria.Op.GT); - activeScheduleSearch.cp(); - activeScheduleSearch.done(); - - scheduleSearchByVmIdAndIds = createSearchBuilder(); - scheduleSearchByVmIdAndIds.and(ApiConstants.ID, scheduleSearchByVmIdAndIds.entity().getId(), SearchCriteria.Op.IN); - scheduleSearchByVmIdAndIds.and(ApiConstants.VIRTUAL_MACHINE_ID, scheduleSearchByVmIdAndIds.entity().getVmId(), SearchCriteria.Op.EQ); - scheduleSearchByVmIdAndIds.done(); - - scheduleSearch = createSearchBuilder(); - scheduleSearch.and(ApiConstants.ID, scheduleSearch.entity().getId(), SearchCriteria.Op.EQ); - scheduleSearch.and(ApiConstants.VIRTUAL_MACHINE_ID, scheduleSearch.entity().getVmId(), SearchCriteria.Op.EQ); - scheduleSearch.and(ApiConstants.ACTION, scheduleSearch.entity().getAction(), SearchCriteria.Op.EQ); - scheduleSearch.and(ApiConstants.ENABLED, scheduleSearch.entity().getEnabled(), SearchCriteria.Op.EQ); - scheduleSearch.done(); - - } - - @Override - public List listAllActiveSchedules() { - // WHERE enabled = true AND (end_date IS NULL OR end_date > current_date) - SearchCriteria sc = activeScheduleSearch.create(); - sc.setParameters(ApiConstants.ENABLED, true); - sc.setParameters(ApiConstants.END_DATE, new Date()); - return search(sc, null); - } - - @Override - public long removeSchedulesForVmIdAndIds(Long vmId, List ids) { - SearchCriteria sc = scheduleSearchByVmIdAndIds.create(); - sc.setParameters(ApiConstants.ID, ids.toArray()); - sc.setParameters(ApiConstants.VIRTUAL_MACHINE_ID, vmId); - return remove(sc); - } - - @Override - public Pair, Integer> searchAndCount(Long id, Long vmId, VMSchedule.Action action, Boolean enabled, Long offset, Long limit) { - SearchCriteria sc = scheduleSearch.create(); - - if (id != null) { - sc.setParameters(ApiConstants.ID, id); - } - if (enabled != null) { - sc.setParameters(ApiConstants.ENABLED, enabled); - } - if (action != null) { - sc.setParameters(ApiConstants.ACTION, action); - } - sc.setParameters(ApiConstants.VIRTUAL_MACHINE_ID, vmId); - - Filter filter = new Filter(VMScheduleVO.class, ApiConstants.ID, false, offset, limit); - return searchAndCount(sc, filter); - } - - @Override - public SearchCriteria getSearchCriteriaForVMId(Long vmId) { - SearchCriteria sc = scheduleSearch.create(); - sc.setParameters(ApiConstants.VIRTUAL_MACHINE_ID, vmId); - return sc; - } -} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduledJobDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduledJobDaoImpl.java deleted file mode 100644 index 50a2b12fd774..000000000000 --- a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduledJobDaoImpl.java +++ /dev/null @@ -1,95 +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.vm.schedule.dao; - -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.vm.schedule.VMScheduledJobVO; -import org.apache.commons.lang3.time.DateUtils; -import org.springframework.stereotype.Component; - -import java.util.Calendar; -import java.util.Date; -import java.util.List; - -@Component -public class VMScheduledJobDaoImpl extends GenericDaoBase implements VMScheduledJobDao { - - private final SearchBuilder jobsToStartSearch; - - private final SearchBuilder expungeJobsBeforeSearch; - - private final SearchBuilder expungeJobForScheduleSearch; - - static final String SCHEDULED_TIMESTAMP = "scheduled_timestamp"; - - static final String VM_SCHEDULE_ID = "vm_schedule_id"; - - public VMScheduledJobDaoImpl() { - super(); - jobsToStartSearch = createSearchBuilder(); - jobsToStartSearch.and(SCHEDULED_TIMESTAMP, jobsToStartSearch.entity().getScheduledTime(), SearchCriteria.Op.EQ); - jobsToStartSearch.and("async_job_id", jobsToStartSearch.entity().getAsyncJobId(), SearchCriteria.Op.NULL); - jobsToStartSearch.done(); - - expungeJobsBeforeSearch = createSearchBuilder(); - expungeJobsBeforeSearch.and(SCHEDULED_TIMESTAMP, expungeJobsBeforeSearch.entity().getScheduledTime(), SearchCriteria.Op.LT); - expungeJobsBeforeSearch.done(); - - expungeJobForScheduleSearch = createSearchBuilder(); - expungeJobForScheduleSearch.and(VM_SCHEDULE_ID, expungeJobForScheduleSearch.entity().getVmScheduleId(), SearchCriteria.Op.IN); - expungeJobForScheduleSearch.and(SCHEDULED_TIMESTAMP, expungeJobForScheduleSearch.entity().getScheduledTime(), SearchCriteria.Op.GTEQ); - expungeJobForScheduleSearch.done(); - } - - /** - * Execution of job wouldn't be at exact seconds. So, we round off and then execute. - */ - @Override - public List listJobsToStart(Date currentTimestamp) { - if (currentTimestamp == null) { - currentTimestamp = new Date(); - } - Date truncatedTs = DateUtils.round(currentTimestamp, Calendar.MINUTE); - - SearchCriteria sc = jobsToStartSearch.create(); - sc.setParameters(SCHEDULED_TIMESTAMP, truncatedTs); - Filter filter = new Filter(VMScheduledJobVO.class, "vmScheduleId", true, null, null); - return search(sc, filter); - } - - @Override - public int expungeJobsForSchedules(List vmScheduleIds, Date dateAfter) { - SearchCriteria sc = expungeJobForScheduleSearch.create(); - sc.setParameters(VM_SCHEDULE_ID, vmScheduleIds.toArray()); - if (dateAfter != null) { - sc.setParameters(SCHEDULED_TIMESTAMP, dateAfter); - } - return expunge(sc); - } - - @Override - public int expungeJobsBefore(Date date) { - SearchCriteria sc = expungeJobsBeforeSearch.create(); - sc.setParameters(SCHEDULED_TIMESTAMP, date); - return expunge(sc); - } -} 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 d308a9e5aaf9..a0dada545617 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 @@ -46,6 +46,7 @@ + @@ -71,6 +72,9 @@ - + + + + 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 7e9d6c3b54f0..932db538f30b 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 @@ -108,6 +108,7 @@ + @@ -116,6 +117,7 @@ + @@ -236,13 +238,11 @@ - - @@ -272,7 +272,12 @@ + + + + + @@ -284,8 +289,9 @@ - - + + + @@ -309,4 +315,19 @@ + + + + + + + + + + + + + + + diff --git a/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_drop_column.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_drop_column.sql new file mode 100644 index 000000000000..31d5b9f500f0 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_drop_column.sql @@ -0,0 +1,28 @@ +-- 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. + +-- in cloud +DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_DROP_COLUMN`; + +-- Error 1091: Can't DROP column; check that column/key exists +CREATE PROCEDURE `cloud`.`IDEMPOTENT_DROP_COLUMN` ( + IN in_table_name VARCHAR(200), + IN in_column_name VARCHAR(200) +) +BEGIN + + DECLARE CONTINUE HANDLER FOR 1091 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name, ' DROP COLUMN ', in_column_name); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; diff --git a/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_drop_unique_key.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_drop_unique_key.sql new file mode 100644 index 000000000000..fdcca6fb4089 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_drop_unique_key.sql @@ -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. + +-- in cloud +DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_DROP_UNIQUE_KEY`; + +CREATE PROCEDURE `cloud`.`IDEMPOTENT_DROP_UNIQUE_KEY` ( + IN in_table_name VARCHAR(200), + IN in_index_name VARCHAR(200) +) +BEGIN + DECLARE CONTINUE HANDLER FOR 1091, 1025 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name, ' DROP KEY ', in_index_name); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; diff --git a/engine/schema/src/main/resources/META-INF/db/procedures/cloud.insert_category_if_not_exists.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.insert_category_if_not_exists.sql new file mode 100644 index 000000000000..a82dc7204c2e --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.insert_category_if_not_exists.sql @@ -0,0 +1,27 @@ +-- 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. + +-- 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; diff --git a/engine/schema/src/main/resources/META-INF/db/procedures/cloud.insert_extension_custom_action_details_if_not_exists.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.insert_extension_custom_action_details_if_not_exists.sql new file mode 100644 index 000000000000..77b162236266 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.insert_extension_custom_action_details_if_not_exists.sql @@ -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. + +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; diff --git a/engine/schema/src/main/resources/META-INF/db/procedures/cloud.insert_extension_custom_action_if_not_exists.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.insert_extension_custom_action_if_not_exists.sql new file mode 100644 index 000000000000..9dbffa630f84 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.insert_extension_custom_action_if_not_exists.sql @@ -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. + +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; diff --git a/engine/schema/src/main/resources/META-INF/db/procedures/cloud.insert_extension_detail_if_not_exists.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.insert_extension_detail_if_not_exists.sql new file mode 100644 index 000000000000..f9d6c5da9512 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.insert_extension_detail_if_not_exists.sql @@ -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. + +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; diff --git a/engine/schema/src/main/resources/META-INF/db/procedures/cloud.insert_extension_if_not_exists.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.insert_extension_if_not_exists.sql new file mode 100644 index 000000000000..8d74f9b2a986 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.insert_extension_if_not_exists.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. + +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; diff --git a/engine/schema/src/main/resources/META-INF/db/procedures/cloud.update_category_for_guest_oses.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.update_category_for_guest_oses.sql new file mode 100644 index 000000000000..87f3a85d27ef --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.update_category_for_guest_oses.sql @@ -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. + +-- 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; diff --git a/engine/schema/src/main/resources/META-INF/db/procedures/cloud.update_new_and_delete_old_category_for_guest_os.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.update_new_and_delete_old_category_for_guest_os.sql new file mode 100644 index 000000000000..42f7aa738cff --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.update_new_and_delete_old_category_for_guest_os.sql @@ -0,0 +1,35 @@ +-- 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. + +-- 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; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-307to410.sql b/engine/schema/src/main/resources/META-INF/db/schema-307to410.sql index 55d78b594377..3b0cfa8e6ce3 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-307to410.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-307to410.sql @@ -3,7 +3,7 @@ -- 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 compliances +-- "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 diff --git a/engine/schema/src/main/resources/META-INF/db/schema-410to420.sql b/engine/schema/src/main/resources/META-INF/db/schema-410to420.sql index 35f73b35d3ca..d62a9bb93034 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-410to420.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-410to420.sql @@ -2305,7 +2305,7 @@ CREATE TABLE `cloud_usage`.`usage_vmsnapshot` ( ) ENGINE=InnoDB CHARSET=utf8; INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'healthcheck.update.interval', '600', 'Time Interval to fetch the LB health check states (in sec)'); -INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Snapshots', 'DEFAULT', 'SnapshotManager', 'kvm.snapshot.enabled', 'false', 'whether snapshot is enabled for KVM hosts'); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Snapshots', 'DEFAULT', 'SnapshotManager', 'kvm.snapshot.enabled', 'true', 'whether snapshot is enabled for KVM hosts'); INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'eip.use.multiple.netscalers', 'false', 'Should be set to true, if there will be multiple NetScaler devices providing EIP service in a zone'); INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Snapshots', 'DEFAULT', 'SnapshotManager', 'snapshot.backup.rightafter', 'true', 'backup snapshot right after snapshot is taken'); 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 3dd6c18f57c5..4405250f2711 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 @@ -31,7 +31,7 @@ CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.account', 'api_key_access', 'boolean CALL `cloud_usage`.`IDEMPOTENT_ADD_COLUMN`('cloud_usage.account', 'api_key_access', 'boolean DEFAULT NULL COMMENT "is api key access allowed for the account" '); -- Create a new group for Usage Server related configurations -INSERT INTO `cloud`.`configuration_group` (`name`, `description`, `precedence`) VALUES ('Usage Server', 'Usage Server related configuration', 9); +INSERT IGNORE INTO `cloud`.`configuration_group` (`name`, `description`, `precedence`) VALUES ('Usage Server', 'Usage Server related configuration', 9); UPDATE `cloud`.`configuration_subgroup` set `group_id` = (SELECT `id` FROM `cloud`.`configuration_group` WHERE `name` = 'Usage Server'), `precedence` = 1 WHERE `name`='Usage'; UPDATE `cloud`.`configuration` SET `group_id` = (SELECT `id` FROM `cloud`.`configuration_group` WHERE `name` = 'Usage Server') where `subgroup_id` = (SELECT `id` FROM `cloud`.`configuration_subgroup` WHERE `name` = 'Usage'); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42020to42030.sql b/engine/schema/src/main/resources/META-INF/db/schema-42020to42030.sql new file mode 100644 index 000000000000..5eec97278bad --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-42020to42030.sql @@ -0,0 +1,28 @@ +-- 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.2.0 to 4.20.3.0 +--; + +ALTER TABLE `cloud`.`template_store_ref` MODIFY COLUMN `download_url` varchar(2048); + +UPDATE `cloud`.`alert` SET type = 33 WHERE name = 'ALERT.VR.PUBLIC.IFACE.MTU'; +UPDATE `cloud`.`alert` SET type = 34 WHERE name = 'ALERT.VR.PRIVATE.IFACE.MTU'; + +-- Update configuration 'kvm.ssh.to.agent' description and is_dynamic fields +UPDATE `cloud`.`configuration` SET description = 'True if the management server will restart the agent service via SSH into the KVM hosts after or during maintenance operations', is_dynamic = 1 WHERE name = 'kvm.ssh.to.agent'; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-42040to42100-cleanup.sql similarity index 93% rename from engine/schema/src/main/resources/META-INF/db/schema-42010to42100cleanup.sql rename to engine/schema/src/main/resources/META-INF/db/schema-42040to42100-cleanup.sql index 9165565f544a..b63e918b389b 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100cleanup.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-42040to42100-cleanup.sql @@ -16,5 +16,5 @@ -- under the License. --; --- Schema upgrade from 4.20.1.0 to 4.21.0.0 +-- Schema upgrade cleanup from 4.20.4.0 to 4.21.0.0 --; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql b/engine/schema/src/main/resources/META-INF/db/schema-42040to42100.sql similarity index 96% rename from engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql rename to engine/schema/src/main/resources/META-INF/db/schema-42040to42100.sql index 167dd92730cc..bf7f3aeaf0f8 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-42040to42100.sql @@ -16,7 +16,7 @@ -- under the License. --; --- Schema upgrade from 4.20.1.0 to 4.21.0.0 +-- Schema upgrade from 4.20.4.0 to 4.21.0.0 --; 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'''); @@ -301,7 +301,7 @@ CALL `cloud`.`IDEMPOTENT_DROP_FOREIGN_KEY`('cloud.service_offering','fk_service_ 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` ( +CREATE TABLE IF NOT EXISTS `cloud`.`netris_providers` ( `id` bigint unsigned NOT NULL auto_increment COMMENT 'id', `uuid` varchar(40), `zone_id` bigint unsigned NOT NULL COMMENT 'Zone ID', @@ -321,11 +321,11 @@ CREATE TABLE `cloud`.`netris_providers` ( ) 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`; +CALL `cloud`.`IDEMPOTENT_DROP_COLUMN`('cloud.network_offerings', 'for_tungsten'); +CALL `cloud`.`IDEMPOTENT_DROP_COLUMN`('cloud.network_offerings', '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`; +CALL `cloud`.`IDEMPOTENT_DROP_COLUMN`('cloud.vpc_offerings', '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`'); @@ -687,22 +687,23 @@ CREATE TABLE IF NOT EXISTS `cloud`.`backup_details` ( 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 "," + SELECT COALESCE( + CAST( + JSON_ARRAYAGG( + JSON_OBJECT( + 'uuid', v.uuid, + 'type', v.volume_type, + 'size', v.size, + 'path', v.path, + 'deviceId', v.device_id, + 'diskOfferingId', doff.uuid, + 'minIops', v.min_iops, + 'maxIops', v.max_iops + ) + ) AS CHAR ), - "]") + '[]' + ) FROM `cloud`.`volumes` v LEFT JOIN `cloud`.`disk_offering` doff ON v.disk_offering_id = doff.id WHERE v.instance_id = vm.id @@ -711,22 +712,23 @@ SET b.backed_volumes = ( -- 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 "," + SELECT COALESCE( + CAST( + JSON_ARRAYAGG( + JSON_OBJECT( + 'uuid', v.uuid, + 'type', v.volume_type, + 'size', v.size, + 'path', v.path, + 'deviceId', v.device_id, + 'diskOfferingId', doff.uuid, + 'minIops', v.min_iops, + 'maxIops', v.max_iops + ) + ) AS CHAR ), - "]") + '[]' + ) FROM `cloud`.`volumes` v LEFT JOIN `cloud`.`disk_offering` doff ON v.disk_offering_id = doff.id WHERE v.instance_id = vm.id 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 index cf3fe2ed7726..fbb2fd079f9c 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-42100to42200.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-42100to42200.sql @@ -19,5 +19,75 @@ -- Schema upgrade from 4.21.0.0 to 4.22.0.0 --; +-- health check status as enum +CALL `cloud`.`IDEMPOTENT_CHANGE_COLUMN`('router_health_check', 'check_result', 'check_result', 'varchar(16) NOT NULL COMMENT "check executions result: SUCCESS, FAILURE, WARNING, UNKNOWN"'); + -- 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)'); + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.snapshot_policy','domain_id', 'BIGINT(20) DEFAULT NULL'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.snapshot_policy','account_id', 'BIGINT(20) DEFAULT NULL'); + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backup_schedule','domain_id', 'BIGINT(20) DEFAULT NULL'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backup_schedule','account_id', 'BIGINT(20) DEFAULT NULL'); + +-- Increase the cache_mode column size from cloud.disk_offering table +CALL `cloud`.`IDEMPOTENT_CHANGE_COLUMN`('cloud.disk_offering', 'cache_mode', 'cache_mode', 'varchar(18) DEFAULT "none" COMMENT "The disk cache mode to use for disks created with this offering"'); + +-- Add uuid column to ldap_configuration table +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.ldap_configuration', 'uuid', 'VARCHAR(40) NOT NULL'); + +-- Populate uuid for existing rows where uuid is NULL or empty +UPDATE `cloud`.`ldap_configuration` SET uuid = UUID() WHERE uuid IS NULL OR uuid = ''; + +-- Add the column cross_zone_instance_creation to cloud.backup_repository. if enabled it means that new Instance can be created on all Zones from Backups on this Repository. +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backup_repository', 'cross_zone_instance_creation', 'TINYINT(1) DEFAULT NULL COMMENT ''Backup Repository can be used for disaster recovery on another zone'''); + +-- Updated display to false for password/token detail of the storage pool details +UPDATE `cloud`.`storage_pool_details` SET display = 0 WHERE name LIKE '%password%'; +UPDATE `cloud`.`storage_pool_details` SET display = 0 WHERE name LIKE '%token%'; + +-- Add csi_enabled column to kubernetes_cluster table to indicate if the cluster is using csi or not +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster', 'csi_enabled', 'TINYINT(1) unsigned NOT NULL DEFAULT 0 COMMENT "true if kubernetes cluster is using csi, false otherwise" '); + +-- VMware to KVM migration improvements +CREATE TABLE IF NOT EXISTS `cloud`.`import_vm_task`( + `id` bigint unsigned NOT NULL auto_increment COMMENT 'id', + `uuid` varchar(40), + `zone_id` bigint unsigned NOT NULL COMMENT 'Zone ID', + `account_id` bigint unsigned NOT NULL COMMENT 'Account ID', + `user_id` bigint unsigned NOT NULL COMMENT 'User ID', + `vm_id` bigint unsigned COMMENT 'VM ID', + `display_name` varchar(255) COMMENT 'Display VM Name', + `vcenter` varchar(255) COMMENT 'VCenter', + `datacenter` varchar(255) COMMENT 'VCenter Datacenter name', + `source_vm_name` varchar(255) COMMENT 'Source VM name on vCenter', + `convert_host_id` bigint unsigned COMMENT 'Convert Host ID', + `import_host_id` bigint unsigned COMMENT 'Import Host ID', + `step` varchar(20) COMMENT 'Importing VM Task Step', + `state` varchar(20) COMMENT 'Importing VM Task State', + `description` varchar(255) COMMENT 'Importing VM Task Description', + `duration` bigint unsigned COMMENT 'Duration in milliseconds for the completed tasks', + `created` datetime NOT NULL COMMENT 'date created', + `updated` datetime COMMENT 'date updated if not null', + `removed` datetime COMMENT 'date removed if not null', + PRIMARY KEY (`id`), + CONSTRAINT `fk_import_vm_task__zone_id` FOREIGN KEY `fk_import_vm_task__zone_id` (`zone_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_import_vm_task__account_id` FOREIGN KEY `fk_import_vm_task__account_id` (`account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_import_vm_task__user_id` FOREIGN KEY `fk_import_vm_task__user_id` (`user_id`) REFERENCES `user`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_import_vm_task__vm_id` FOREIGN KEY `fk_import_vm_task__vm_id` (`vm_id`) REFERENCES `vm_instance`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_import_vm_task__convert_host_id` FOREIGN KEY `fk_import_vm_task__convert_host_id` (`convert_host_id`) REFERENCES `host`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_import_vm_task__import_host_id` FOREIGN KEY `fk_import_vm_task__import_host_id` (`import_host_id`) REFERENCES `host`(`id`) ON DELETE CASCADE, + INDEX `i_import_vm_task__zone_id`(`zone_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CALL `cloud`.`INSERT_EXTENSION_IF_NOT_EXISTS`('MaaS', 'Baremetal Extension for Canonical MaaS written in Python', 'MaaS/maas.py'); +CALL `cloud`.`INSERT_EXTENSION_DETAIL_IF_NOT_EXISTS`('MaaS', 'orchestratorrequirespreparevm', 'true', 0); + +CALL `cloud`.`IDEMPOTENT_DROP_UNIQUE_KEY`('counter', 'uc_counter__provider__source__value'); +CALL `cloud`.`IDEMPOTENT_ADD_UNIQUE_KEY`('cloud.counter', 'uc_counter__provider__source__value__removed', '(provider, source, value, removed)'); + +-- Change scope for configuration - 'use.https.to.upload from' from StoragePool to Zone +UPDATE `cloud`.`configuration` SET `scope` = 2 WHERE `name` = 'use.https.to.upload'; +-- Delete the configuration for 'use.https.to.upload' from StoragePool +DELETE FROM `cloud`.`storage_pool_details` WHERE `name` = 'use.https.to.upload'; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42200to42210-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-42200to42210-cleanup.sql new file mode 100644 index 000000000000..2f104568c140 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-42200to42210-cleanup.sql @@ -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. + +--; +-- Schema upgrade cleanup from 4.22.0.0 to 4.22.1.0 +--; + +-- Entries remaining on `cloud`.`resource_reservation` during the upgrade process are stale, so delete them. +-- This script was added to normalize volume/primary storage reservations that got stuck due to a bug on VM deployment, +-- but it is more interesting to introduce a smarter logic to clean these stale reservations in the future without the need +-- for upgrades (for instance, by having a heartbeat_time column for the reservations and automatically cleaning old entries). +DELETE FROM `cloud`.`resource_reservation`; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42200to42210.sql b/engine/schema/src/main/resources/META-INF/db/schema-42200to42210.sql new file mode 100644 index 000000000000..baa20e9f0ad5 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-42200to42210.sql @@ -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. + +--; +-- Schema upgrade from 4.22.0.0 to 4.22.1.0 +--; + +-- Add vm_id column to usage_event table for volume usage events +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.usage_event','vm_id', 'bigint UNSIGNED NULL COMMENT "VM ID associated with volume usage events"'); +CALL `cloud_usage`.`IDEMPOTENT_ADD_COLUMN`('cloud_usage.usage_event','vm_id', 'bigint UNSIGNED NULL COMMENT "VM ID associated with volume usage events"'); + +-- Add vm_id column to cloud_usage.usage_volume table +CALL `cloud_usage`.`IDEMPOTENT_ADD_COLUMN`('cloud_usage.usage_volume','vm_id', 'bigint UNSIGNED NULL COMMENT "VM ID associated with the volume usage"'); + +ALTER TABLE `cloud`.`template_store_ref` MODIFY COLUMN `download_url` varchar(2048); + +UPDATE `cloud`.`alert` SET type = 33 WHERE name = 'ALERT.VR.PUBLIC.IFACE.MTU'; +UPDATE `cloud`.`alert` SET type = 34 WHERE name = 'ALERT.VR.PRIVATE.IFACE.MTU'; + +-- Update configuration 'kvm.ssh.to.agent' description and is_dynamic fields +UPDATE `cloud`.`configuration` SET description = 'True if the management server will restart the agent service via SSH into the KVM hosts after or during maintenance operations', is_dynamic = 1 WHERE name = 'kvm.ssh.to.agent'; + +-- Sanitize legacy network-level addressing fields for Public networks +UPDATE `cloud`.`networks` +SET `broadcast_uri` = NULL, + `gateway` = NULL, + `cidr` = NULL, + `ip6_gateway` = NULL, + `ip6_cidr` = NULL +WHERE `traffic_type` = 'Public'; + +UPDATE `cloud`.`vm_template` SET guest_os_id = 99 WHERE name = 'kvm-default-vm-import-dummy-template'; + +-- Update existing vm_template records with NULL type to "USER" +UPDATE `cloud`.`vm_template` SET `type` = 'USER' WHERE `type` IS NULL; + +-- remove unused config item +DELETE FROM `cloud`.`configuration` WHERE name = 'consoleproxy.cmd.port'; + +-- Drops the unused "backup_interval_type" column of the "cloud.backups" table +ALTER TABLE `cloud`.`backups` DROP COLUMN `backup_interval_type`; + +-- Update `user.password.reset.mail.template` configuration value to match new logic +UPDATE `cloud`.`configuration` +SET value = CONCAT_WS('\n', 'Hello {{username}}!', 'You have requested to reset your password. Please click the following link to reset your password:', '{{{resetLink}}}', 'If you did not request a password reset, please ignore this email.', '', 'Regards,', 'The CloudStack Team') +WHERE name = 'user.password.reset.mail.template' + AND value IN (CONCAT_WS('\n', 'Hello {{username}}!', 'You have requested to reset your password. Please click the following link to reset your password:', 'http://{{{resetLink}}}', 'If you did not request a password reset, please ignore this email.', '', 'Regards,', 'The CloudStack Team'), CONCAT_WS('\n', 'Hello {{username}}!', 'You have requested to reset your password. Please click the following link to reset your password:', '{{{domainUrl}}}{{{resetLink}}}', 'If you did not request a password reset, please ignore this email.', '', 'Regards,', 'The CloudStack Team')); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-42210to42300-cleanup.sql similarity index 93% rename from engine/schema/src/main/resources/META-INF/db/schema-42010to42100-cleanup.sql rename to engine/schema/src/main/resources/META-INF/db/schema-42210to42300-cleanup.sql index 5f257f2965bd..e2b066af7800 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100-cleanup.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-42210to42300-cleanup.sql @@ -16,5 +16,5 @@ -- under the License. --; --- Schema upgrade cleanup from 4.20.1.0 to 4.21.0.0 +-- Schema upgrade cleanup from 4.22.1.0 to 4.23.0.0 --; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql b/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql new file mode 100644 index 000000000000..ab5ac7b2b875 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql @@ -0,0 +1,648 @@ +-- 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.22.1.0 to 4.23.0.0 +--; + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.oauth_provider', 'domain_id', 'bigint unsigned DEFAULT NULL COMMENT "NULL for global provider, domain ID for domain-specific" AFTER `redirect_uri`'); +CALL `cloud`.`IDEMPOTENT_ADD_FOREIGN_KEY`('cloud.oauth_provider', 'fk_oauth_provider__domain_id', '(`domain_id`)', '`domain`(`id`)'); +CALL `cloud`.`IDEMPOTENT_ADD_KEY`('i_oauth_provider__domain_id', 'cloud.oauth_provider', '(`domain_id`)'); + +CALL `cloud`.`IDEMPOTENT_ADD_UNIQUE_KEY`('cloud.oauth_provider', 'uk_oauth_provider__provider_domain', '(`provider`, `domain_id`)'); + +CREATE TABLE `cloud`.`backup_offering_details` ( + `id` bigint unsigned NOT NULL auto_increment, + `backup_offering_id` bigint unsigned NOT NULL COMMENT 'Backup offering id', + `name` varchar(255) NOT NULL, + `value` varchar(1024) NOT NULL, + `display` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Should detail be displayed to the end user', + PRIMARY KEY (`id`), + CONSTRAINT `fk_offering_details__backup_offering_id` FOREIGN KEY `fk_offering_details__backup_offering_id`(`backup_offering_id`) REFERENCES `backup_offering`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- Update value to random for the config 'vm.allocation.algorithm' or 'volume.allocation.algorithm' if configured as userconcentratedpod_random +-- Update value to firstfit for the config 'vm.allocation.algorithm' or 'volume.allocation.algorithm' if configured as userconcentratedpod_firstfit +UPDATE `cloud`.`configuration` SET value='random' WHERE name IN ('vm.allocation.algorithm', 'volume.allocation.algorithm') AND value='userconcentratedpod_random'; +UPDATE `cloud`.`configuration` SET value='firstfit' WHERE name IN ('vm.allocation.algorithm', 'volume.allocation.algorithm') AND value='userconcentratedpod_firstfit'; + +-- Add Windows Server 2025 guest OS and mappings +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows Server 2025 (64-bit)', 'VMware', '7.0', 'windows2022srvNext_64Guest'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows Server 2025 (64-bit)', 'VMware', '7.0.1.0', 'windows2022srvNext_64Guest'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows Server 2025 (64-bit)', 'VMware', '7.0.2.0', 'windows2022srvNext_64Guest'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows Server 2025 (64-bit)', 'VMware', '7.0.3.0', 'windows2022srvNext_64Guest'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows Server 2025 (64-bit)', 'VMware', '8.0', 'windows2022srvNext_64Guest'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows Server 2025 (64-bit)', 'VMware', '8.0.0.1', 'windows2022srvNext_64Guest'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows Server 2025 (64-bit)', 'VMware', '8.0.0.2', 'windows2022srvNext_64Guest'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows Server 2025 (64-bit)', 'VMware', '8.0.0.3', 'windows2022srvNext_64Guest'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows Server 2025 (64-bit)', 'VMware', '8.0.1', 'windows2022srvNext_64Guest'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows Server 2025 (64-bit)', 'VMware', '8.0.2', 'windows2022srvNext_64Guest'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows Server 2025 (64-bit)', 'VMware', '8.0.3', 'windows2022srvNext_64Guest'); + +-- Create kubernetes_cluster_affinity_group_map table for CKS per-node-type affinity groups +CREATE TABLE IF NOT EXISTS `cloud`.`kubernetes_cluster_affinity_group_map` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `cluster_id` bigint unsigned NOT NULL COMMENT 'kubernetes cluster id', + `node_type` varchar(32) NOT NULL COMMENT 'CONTROL, WORKER, or ETCD', + `affinity_group_id` bigint unsigned NOT NULL COMMENT 'affinity group id', + PRIMARY KEY (`id`), + CONSTRAINT `fk_kubernetes_cluster_ag_map__cluster_id` FOREIGN KEY (`cluster_id`) REFERENCES `kubernetes_cluster`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_kubernetes_cluster_ag_map__ag_id` FOREIGN KEY (`affinity_group_id`) REFERENCES `affinity_group`(`id`) ON DELETE CASCADE, + INDEX `i_kubernetes_cluster_ag_map__cluster_id`(`cluster_id`), + INDEX `i_kubernetes_cluster_ag_map__ag_id`(`affinity_group_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- Create webhook_filter table +DROP TABLE IF EXISTS `cloud`.`webhook_filter`; +CREATE TABLE IF NOT EXISTS `cloud`.`webhook_filter` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id of the webhook filter', + `uuid` varchar(255) COMMENT 'uuid of the webhook filter', + `webhook_id` bigint unsigned NOT NULL COMMENT 'id of the webhook', + `type` varchar(20) COMMENT 'type of the filter', + `mode` varchar(20) COMMENT 'mode of the filter', + `match_type` varchar(20) COMMENT 'match type of the filter', + `value` varchar(256) NOT NULL COMMENT 'value of the filter used for matching', + `created` datetime NOT NULL COMMENT 'date created', + PRIMARY KEY (`id`), + INDEX `i_webhook_filter__webhook_id`(`webhook_id`), + CONSTRAINT `fk_webhook_filter__webhook_id` FOREIGN KEY(`webhook_id`) REFERENCES `webhook`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- "api_keypair" table for API and secret keys +CREATE TABLE IF NOT EXISTS `cloud`.`api_keypair` ( + `id` bigint(20) unsigned NOT NULL auto_increment, + `uuid` varchar(40) UNIQUE NOT NULL, + `name` varchar(255) NOT NULL, + `domain_id` bigint(20) unsigned NOT NULL, + `account_id` bigint(20) unsigned NOT NULL, + `user_id` bigint(20) unsigned NOT NULL, + `start_date` datetime, + `end_date` datetime, + `description` varchar(1024), + `api_key` varchar(255) NOT NULL, + `secret_key` varchar(255) NOT NULL, + `created` datetime NOT NULL, + `removed` datetime, + PRIMARY KEY (`id`), + CONSTRAINT `fk_api_keypair__user_id` FOREIGN KEY(`user_id`) REFERENCES `cloud`.`user`(`id`), + CONSTRAINT `fk_api_keypair__account_id` FOREIGN KEY(`account_id`) REFERENCES `cloud`.`account`(`id`), + CONSTRAINT `fk_api_keypair__domain_id` FOREIGN KEY(`domain_id`) REFERENCES `cloud`.`domain`(`id`) +); + +-- "api_keypair_permissions" table for API key pairs permissions +CREATE TABLE IF NOT EXISTS `cloud`.`api_keypair_permissions` ( + `id` bigint(20) unsigned NOT NULL auto_increment, + `uuid` varchar(40) UNIQUE, + `sort_order` bigint(20) unsigned NOT NULL DEFAULT 0, + `rule` varchar(255) NOT NULL, + `api_keypair_id` bigint(20) unsigned NOT NULL, + `permission` varchar(255) NOT NULL, + `description` varchar(255), + PRIMARY KEY (`id`), + CONSTRAINT `fk_keypair_permissions__api_keypair_id` FOREIGN KEY(`api_keypair_id`) REFERENCES `cloud`.`api_keypair`(`id`) +); + +-- Populate "api_keypair" table with existing user API keys +INSERT INTO `cloud`.`api_keypair` (uuid, user_id, domain_id, account_id, api_key, secret_key, created, name) +SELECT UUID(), user.id, account.domain_id, account.id, user.api_key, user.secret_key, NOW(), 'Active key pair' +FROM `cloud`.`user` AS user +JOIN `cloud`.`account` AS account ON user.account_id = account.id +WHERE user.api_key IS NOT NULL AND user.secret_key IS NOT NULL; + +-- Drop API keys from user table +ALTER TABLE `cloud`.`user` DROP COLUMN api_key, DROP COLUMN secret_key; + +-- Grant access to the "deleteUserKeys" and "listUserKeyRules" APIs to the "User", "Domain Admin" and "Resource Admin" roles, similarly to the "registerUserKeys" API +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('User', 'deleteUserKeys', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Domain Admin', 'deleteUserKeys', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Resource Admin', 'deleteUserKeys', 'ALLOW'); + +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('User', 'listUserKeyRules', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Domain Admin', 'listUserKeyRules', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Resource Admin', 'listUserKeyRules', 'ALLOW'); + +-- Add conserve mode for VPC offerings +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','conserve_mode', 'tinyint(1) unsigned NULL DEFAULT 0 COMMENT ''True if the VPC offering is IP conserve mode enabled, allowing public IP services to be used across multiple VPC tiers'' '); + +-- KMS HSM Profiles (Generic table for PKCS#11, KMIP, etc.) +-- Scoped to account (user-provided) or global/zone (admin-provided) +CREATE TABLE IF NOT EXISTS `cloud`.`kms_hsm_profiles` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + `uuid` VARCHAR(40) NOT NULL, + `name` VARCHAR(255) NOT NULL, + `protocol` VARCHAR(32) NOT NULL COMMENT 'Protocol for the HSM Profile', + + -- Scoping + `account_id` BIGINT UNSIGNED COMMENT 'null = admin-provided (available to all accounts)', + `domain_id` BIGINT UNSIGNED COMMENT 'null = zone/global scope', + `zone_id` BIGINT UNSIGNED COMMENT 'null = global scope', + + -- Metadata + `vendor_name` VARCHAR(64) COMMENT 'HSM vendor', + `enabled` BOOLEAN NOT NULL DEFAULT TRUE, + `is_public` BOOLEAN NOT NULL DEFAULT FALSE COMMENT 'Public profile. Available to all accounts', + `created` DATETIME NOT NULL, + `removed` DATETIME, + + PRIMARY KEY (`id`), + UNIQUE KEY `uk_uuid` (`uuid`), + UNIQUE KEY `uk_account_name` (`account_id`, `name`, `removed`), + INDEX `idx_protocol_enabled` (`protocol`, `enabled`, `removed`), + INDEX `idx_scoping` (`account_id`, `domain_id`, `zone_id`, `removed`), + CONSTRAINT `fk_kms_hsm_profiles__account_id` FOREIGN KEY (`account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_kms_hsm_profiles__domain_id` FOREIGN KEY (`domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_kms_hsm_profiles__zone_id` FOREIGN KEY (`zone_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='HSM profiles for KMS providers'; + +-- KMS HSM Profile Details (Protocol-specific configuration) +CREATE TABLE IF NOT EXISTS `cloud`.`kms_hsm_profile_details` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + `profile_id` BIGINT UNSIGNED NOT NULL COMMENT 'HSM profile ID', + `name` VARCHAR(255) NOT NULL COMMENT 'Config key (e.g. library_path, endpoint, pin, cert_content)', + `value` TEXT NOT NULL COMMENT 'Config value (encrypted if sensitive)', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_profile_name` (`profile_id`, `name`), + CONSTRAINT `fk_kms_hsm_profile_details__profile_id` FOREIGN KEY (`profile_id`) REFERENCES `kms_hsm_profiles`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Details for HSM profiles (key-value configuration)'; + +-- KMS Keys (Key Encryption Key Metadata) +-- Account-scoped KEKs for envelope encryption +CREATE TABLE IF NOT EXISTS `cloud`.`kms_keys` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + `uuid` VARCHAR(40) NOT NULL COMMENT 'UUID - user-facing identifier', + `name` VARCHAR(255) NOT NULL COMMENT 'User-friendly name', + `description` VARCHAR(1024) COMMENT 'User description', + `kek_label` VARCHAR(255) NOT NULL COMMENT 'Provider-specific KEK label/ID', + `purpose` VARCHAR(32) NOT NULL COMMENT 'Key purpose (VOLUME_ENCRYPTION, TLS_CERT)', + `account_id` BIGINT UNSIGNED NOT NULL COMMENT 'Owning account', + `domain_id` BIGINT UNSIGNED NOT NULL COMMENT 'Owning domain', + `zone_id` BIGINT UNSIGNED NOT NULL COMMENT 'Zone where key is valid', + `algorithm` VARCHAR(64) NOT NULL DEFAULT 'AES/GCM/NoPadding' COMMENT 'Encryption algorithm', + `key_bits` INT NOT NULL DEFAULT 256 COMMENT 'Key size in bits', + `enabled` TINYINT(1) NOT NULL DEFAULT 1 COMMENT 'Whether the key is enabled for new cryptographic operations', + `hsm_profile_id` BIGINT UNSIGNED NOT NULL COMMENT 'Current HSM profile ID for this key', + `created` DATETIME NOT NULL COMMENT 'Creation timestamp', + `removed` DATETIME COMMENT 'Removal timestamp for soft delete', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_uuid` (`uuid`), + INDEX `idx_account_purpose` (`account_id`, `purpose`, `enabled`), + INDEX `idx_domain_purpose` (`domain_id`, `purpose`, `enabled`), + INDEX `idx_zone_enabled` (`zone_id`, `enabled`), + CONSTRAINT `fk_kms_keys__account_id` FOREIGN KEY (`account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_kms_keys__domain_id` FOREIGN KEY (`domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_kms_keys__zone_id` FOREIGN KEY (`zone_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_kms_keys__hsm_profile_id` FOREIGN KEY (`hsm_profile_id`) REFERENCES `kms_hsm_profiles`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='KMS Key (KEK) metadata - account-scoped keys for envelope encryption'; + +-- KMS KEK Versions (multiple KEKs per KMS key for gradual rotation) +-- Supports multiple KEK versions per logical KMS key during rotation +CREATE TABLE IF NOT EXISTS `cloud`.`kms_kek_versions` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + `uuid` VARCHAR(40) NOT NULL COMMENT 'UUID', + `kms_key_id` BIGINT UNSIGNED NOT NULL COMMENT 'Reference to kms_keys table', + `version_number` INT NOT NULL COMMENT 'Version number (1, 2, 3, ...)', + `kek_label` VARCHAR(255) NOT NULL COMMENT 'Provider-specific KEK label/ID for this version', + `status` VARCHAR(32) NOT NULL DEFAULT 'Active' COMMENT 'Active, Previous, Archived', + `hsm_profile_id` BIGINT UNSIGNED COMMENT 'HSM profile where this KEK version is stored', + `created` DATETIME NOT NULL COMMENT 'Creation timestamp', + `removed` DATETIME COMMENT 'Removal timestamp for soft delete', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_uuid` (`uuid`), + UNIQUE KEY `uk_kms_key_version` (`kms_key_id`, `version_number`, `removed`), + INDEX `idx_kms_key_status` (`kms_key_id`, `status`, `removed`), + INDEX `idx_kek_label` (`kek_label`), + CONSTRAINT `fk_kms_kek_versions__kms_key_id` FOREIGN KEY (`kms_key_id`) REFERENCES `kms_keys`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_kms_kek_versions__hsm_profile_id` FOREIGN KEY (`hsm_profile_id`) REFERENCES `kms_hsm_profiles`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='KEK versions for a KMS key - supports gradual rotation'; + +-- KMS Wrapped Keys (Data Encryption Keys) +-- Generic table for wrapped DEKs - references kms_keys for metadata and kek_versions for specific KEK version +CREATE TABLE IF NOT EXISTS `cloud`.`kms_wrapped_key` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + `uuid` VARCHAR(40) NOT NULL COMMENT 'UUID', + `kms_key_id` BIGINT UNSIGNED COMMENT 'Reference to kms_keys table', + `kek_version_id` BIGINT UNSIGNED COMMENT 'Reference to kms_kek_versions table', + `zone_id` BIGINT UNSIGNED NOT NULL COMMENT 'Zone ID for zone-scoped keys', + `wrapped_blob` VARBINARY(4096) NOT NULL COMMENT 'Encrypted DEK material', + `created` DATETIME NOT NULL COMMENT 'Creation timestamp', + `removed` DATETIME COMMENT 'Removal timestamp for soft delete', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_uuid` (`uuid`), + INDEX `idx_kms_key_id` (`kms_key_id`, `removed`), + INDEX `idx_kek_version_id` (`kek_version_id`, `removed`), + INDEX `idx_zone_id` (`zone_id`, `removed`), + CONSTRAINT `fk_kms_wrapped_key__kms_key_id` FOREIGN KEY (`kms_key_id`) REFERENCES `kms_keys`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_kms_wrapped_key__kek_version_id` FOREIGN KEY (`kek_version_id`) REFERENCES `kms_kek_versions`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_kms_wrapped_key__zone_id` FOREIGN KEY (`zone_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='KMS wrapped encryption keys (DEKs) - references kms_keys for KEK metadata and kek_versions for specific version'; + +-- Add KMS key reference to volumes table (which KMS key was used) +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.volumes', 'kms_key_id', 'BIGINT UNSIGNED COMMENT ''KMS key ID used for volume encryption'''); +CALL `cloud`.`IDEMPOTENT_ADD_FOREIGN_KEY`('cloud.volumes', 'fk_volumes__kms_key_id', '(kms_key_id)', '`kms_keys`(`id`)'); + +-- Add KMS wrapped key reference to volumes table +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.volumes', 'kms_wrapped_key_id', 'BIGINT UNSIGNED COMMENT ''KMS wrapped key ID for volume encryption'''); +CALL `cloud`.`IDEMPOTENT_ADD_FOREIGN_KEY`('cloud.volumes', 'fk_volumes__kms_wrapped_key_id', '(kms_wrapped_key_id)', '`kms_wrapped_key`(`id`)'); + +-- KMS Database Provider KEK Objects (PKCS#11-like object storage) +-- Stores KEKs for the database KMS provider in a PKCS#11-compatible format +CREATE TABLE IF NOT EXISTS `cloud`.`kms_database_kek_objects` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Object handle (PKCS#11 CKA_HANDLE)', + `uuid` VARCHAR(40) NOT NULL COMMENT 'UUID', + -- PKCS#11 Object Class (CKA_CLASS) + `object_class` VARCHAR(32) NOT NULL DEFAULT 'CKO_SECRET_KEY' COMMENT 'PKCS#11 object class (CKO_SECRET_KEY, CKO_PRIVATE_KEY, etc.)', + -- PKCS#11 Label (CKA_LABEL) - human-readable identifier + `label` VARCHAR(255) NOT NULL COMMENT 'PKCS#11 label (CKA_LABEL) - human-readable identifier', + -- PKCS#11 ID (CKA_ID) - application-defined identifier + `object_id` VARBINARY(64) COMMENT 'PKCS#11 object ID (CKA_ID) - application-defined identifier', + -- Key Type (CKA_KEY_TYPE) + `key_type` VARCHAR(32) NOT NULL DEFAULT 'CKK_AES' COMMENT 'PKCS#11 key type (CKK_AES, CKK_RSA, etc.)', + -- Key Material (CKA_VALUE) - encrypted KEK material + `key_material` VARBINARY(512) NOT NULL COMMENT 'PKCS#11 key value (CKA_VALUE) - encrypted KEK material', + -- Key Attributes (PKCS#11 boolean attributes) + `is_sensitive` BOOLEAN NOT NULL DEFAULT TRUE COMMENT 'PKCS#11 CKA_SENSITIVE - key material is sensitive', + `is_extractable` BOOLEAN NOT NULL DEFAULT FALSE COMMENT 'PKCS#11 CKA_EXTRACTABLE - key can be extracted', + `is_token` BOOLEAN NOT NULL DEFAULT TRUE COMMENT 'PKCS#11 CKA_TOKEN - object is on token (persistent)', + `is_private` BOOLEAN NOT NULL DEFAULT TRUE COMMENT 'PKCS#11 CKA_PRIVATE - object is private', + `is_modifiable` BOOLEAN NOT NULL DEFAULT FALSE COMMENT 'PKCS#11 CKA_MODIFIABLE - object can be modified', + `is_copyable` BOOLEAN NOT NULL DEFAULT FALSE COMMENT 'PKCS#11 CKA_COPYABLE - object can be copied', + `is_destroyable` BOOLEAN NOT NULL DEFAULT TRUE COMMENT 'PKCS#11 CKA_DESTROYABLE - object can be destroyed', + `always_sensitive` BOOLEAN NOT NULL DEFAULT TRUE COMMENT 'PKCS#11 CKA_ALWAYS_SENSITIVE - key was always sensitive', + `never_extractable` BOOLEAN NOT NULL DEFAULT TRUE COMMENT 'PKCS#11 CKA_NEVER_EXTRACTABLE - key was never extractable', + -- Key Metadata + `purpose` VARCHAR(32) NOT NULL COMMENT 'Key purpose (VOLUME_ENCRYPTION, TLS_CERT)', + `key_bits` INT NOT NULL COMMENT 'Key size in bits (128, 192, 256)', + `algorithm` VARCHAR(64) NOT NULL DEFAULT 'AES/GCM/NoPadding' COMMENT 'Encryption algorithm', + -- Validity Dates (PKCS#11 CKA_START_DATE, CKA_END_DATE) + `start_date` DATETIME COMMENT 'PKCS#11 CKA_START_DATE - key validity start', + `end_date` DATETIME COMMENT 'PKCS#11 CKA_END_DATE - key validity end', + -- Lifecycle + `created` DATETIME NOT NULL COMMENT 'Creation timestamp', + `last_used` DATETIME COMMENT 'Last usage timestamp', + `removed` DATETIME COMMENT 'Removal timestamp for soft delete', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_uuid` (`uuid`), + UNIQUE KEY `uk_label_removed` (`label`, `removed`), + INDEX `idx_purpose_removed` (`purpose`, `removed`), + INDEX `idx_key_type` (`key_type`, `removed`), + INDEX `idx_object_class` (`object_class`, `removed`), + INDEX `idx_created` (`created`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='KMS Database Provider KEK Objects - PKCS#11-like object storage for KEKs'; + +--- Disable/enable NICs +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.nics','enabled', 'TINYINT(1) NOT NULL DEFAULT 1 COMMENT ''Indicates whether the NIC is enabled or not'' '); + +--- Add URLs for OAuth provider +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.oauth_provider','authorize_url', 'VARCHAR(255) DEFAULT NULL COMMENT ''Authorize URL for OAuth initialization'' '); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.oauth_provider','token_url', 'VARCHAR(255) DEFAULT NULL COMMENT ''Token URL for OAuth finalization'' '); + +--- Quota tariff/usage mapping +CREATE TABLE IF NOT EXISTS `cloud_usage`.`quota_tariff_usage` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `tariff_id` bigint(20) unsigned NOT NULL COMMENT 'ID of the tariff of the Quota usage detail calculated, foreign key to quota_tariff table', + `quota_usage_id` bigint(20) unsigned NOT NULL COMMENT 'ID of the aggregation of Quota usage details, foreign key to quota_usage table', + `quota_used` decimal(20,8) NOT NULL COMMENT 'Amount of quota used', + PRIMARY KEY (`id`), + CONSTRAINT `fk_quota_tariff_usage__tariff_id` FOREIGN KEY (`tariff_id`) REFERENCES `cloud_usage`.`quota_tariff` (`id`), + CONSTRAINT `fk_quota_tariff_usage__quota_usage_id` FOREIGN KEY (`quota_usage_id`) REFERENCES `cloud_usage`.`quota_usage` (`id`)); + +--- Per-VM ISO attachments. user_vm.iso_id remains as the primary/bootable ISO pointer. +CREATE TABLE IF NOT EXISTS `cloud`.`vm_iso_map` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `vm_id` bigint(20) unsigned NOT NULL COMMENT 'foreign key to user_vm', + `iso_id` bigint(20) unsigned NOT NULL COMMENT 'foreign key to vm_template (ISOs are templates of format ISO)', + `device_seq` int(10) unsigned NOT NULL COMMENT 'cdrom slot index used to derive the libvirt device label (3=hdc, 4=hdd)', + `created` datetime NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uc_vm_iso_map__vm_iso` (`vm_id`, `iso_id`), + UNIQUE KEY `uc_vm_iso_map__vm_seq` (`vm_id`, `device_seq`), + CONSTRAINT `fk_vm_iso_map__vm_id` FOREIGN KEY (`vm_id`) REFERENCES `cloud`.`user_vm` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_vm_iso_map__iso_id` FOREIGN KEY (`iso_id`) REFERENCES `cloud`.`vm_template` (`id`) +); + +-- Add the 'keep_mac_address_on_public_nic' column to the 'cloud.networks' and 'cloud.vpc' tables +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.networks', 'keep_mac_address_on_public_nic', 'TINYINT(1) NOT NULL DEFAULT 1'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc', 'keep_mac_address_on_public_nic', 'TINYINT(1) NOT NULL DEFAULT 1'); + +-- Creates the 'kvm.memory.dynamic.scaling.capacity' and, for already active ACS environments, +-- initializes it with the value of the setting 'vm.serviceoffering.ram.size.max' +INSERT INTO `cloud`.`configuration` (`category`, `instance`, `component`, `name`, `value`, `default_value`, `updated`, `scope`, `is_dynamic`, `group_id`, `subgroup_id`, `display_text`, `description`) +SELECT 'Advanced', 'DEFAULT', 'CapacityManager', 'kvm.memory.dynamic.scaling.capacity', `cfg`.`value`, 0, NULL, 4, 1, 6, 27, + 'KVM memory dynamic scaling capacity', 'Defines the maximum memory capacity in MiB for which VMs can be dynamically scaled to with KVM. The ''kvm.memory.dynamic.scaling.capacity'' setting''s value will be used to define the value of the '''' element of domain XMLs. If it is set to a value less than or equal to ''0'', then the host''s memory capacity will be considered.' +FROM `cloud`.`configuration` `cfg` +WHERE NOT EXISTS (SELECT 1 FROM `cloud`.`configuration` WHERE `name` = 'kvm.memory.dynamic.scaling.capacity') + AND `cfg`.`name` = 'vm.serviceoffering.ram.size.max'; + +-- Creates the 'kvm.cpu.dynamic.scaling.capacity' and, for already active ACS environments, +-- initializes it with the value of the setting 'vm.serviceoffering.cpu.cores.max' +INSERT INTO `cloud`.`configuration` (`category`, `instance`, `component`, `name`, `value`, `default_value`, `updated`, `scope`, `is_dynamic`, `group_id`, `subgroup_id`, `display_text`, `description`) +SELECT 'Advanced', 'DEFAULT', 'CapacityManager', 'kvm.cpu.dynamic.scaling.capacity', `cfg`.`value`, 0, NULL, 4, 1, 6, 27, + 'KVM CPU dynamic scaling capacity', 'Defines the maximum vCPU capacity for which VMs can be dynamically scaled to with KVM. The ''kvm.cpu.dynamic.scaling.capacity'' setting''s value will be used to define the value of the '''' element of domain XMLs. If it is set to a value less than or equal to ''0'', then the host''s CPU cores capacity will be considered.' +FROM `cloud`.`configuration` `cfg` +WHERE NOT EXISTS (SELECT 1 FROM `cloud`.`configuration` WHERE `name` = 'kvm.cpu.dynamic.scaling.capacity') + AND `cfg`.`name` = 'vm.serviceoffering.cpu.cores.max'; + +-- Generalise VM schedule tables into resource-agnostic resource_schedule / resource_scheduled_job. +-- Step 1: rename vm_schedule → resource_schedule, rename vm_id → resource_id, add resource_type column. +ALTER TABLE `cloud`.`vm_schedule` + DROP FOREIGN KEY `fk_vm_schedule__vm_id`, + DROP INDEX `i_vm_schedule__vm_id`, + DROP INDEX `i_vm_schedule__enabled_end_date`, + CHANGE COLUMN `vm_id` `resource_id` bigint unsigned NOT NULL COMMENT 'id of the scheduled resource', + ADD COLUMN `resource_type` varchar(64) NOT NULL DEFAULT 'VirtualMachine' COMMENT 'type of the scheduled resource' AFTER `uuid`; + +RENAME TABLE `cloud`.`vm_schedule` TO `cloud`.`resource_schedule`; + +ALTER TABLE `cloud`.`resource_schedule` + ADD INDEX `i_resource_schedule__resource` (`resource_type`, `resource_id`), + ADD INDEX `i_resource_schedule__enabled_end_date` (`enabled`, `end_date`); + +-- Step 2: rename vm_scheduled_job → resource_scheduled_job, rename columns. +ALTER TABLE `cloud`.`vm_scheduled_job` + DROP FOREIGN KEY `fk_vm_scheduled_job__vm_id`, + DROP FOREIGN KEY `fk_vm_scheduled_job__vm_schedule_id`, + DROP INDEX `i_vm_scheduled_job__vm_id`, + DROP INDEX `i_vm_scheduled_job__scheduled_timestamp`, + DROP INDEX `vm_schedule_id`, + CHANGE COLUMN `vm_id` `resource_id` bigint unsigned NOT NULL COMMENT 'id of the scheduled resource', + CHANGE COLUMN `vm_schedule_id` `schedule_id` bigint unsigned NOT NULL COMMENT 'id of the resource_schedule row', + ADD COLUMN `resource_type` varchar(64) NOT NULL DEFAULT 'VirtualMachine' COMMENT 'type of the scheduled resource' AFTER `uuid`; + +RENAME TABLE `cloud`.`vm_scheduled_job` TO `cloud`.`resource_scheduled_job`; + +ALTER TABLE `cloud`.`resource_scheduled_job` + ADD UNIQUE KEY `uc_resource_scheduled_job__schedule_timestamp` (`schedule_id`, `scheduled_timestamp`), + ADD INDEX `i_resource_scheduled_job__resource` (`resource_type`, `resource_id`), + ADD INDEX `i_resource_scheduled_job__scheduled_timestamp` (`scheduled_timestamp`), + ADD CONSTRAINT `fk_resource_scheduled_job__schedule_id` FOREIGN KEY (`schedule_id`) REFERENCES `resource_schedule`(`id`) ON DELETE CASCADE; + +-- Step 3: details table for action-specific parameters (used by the generic resource schedule API in Commit 2). +CREATE TABLE IF NOT EXISTS `cloud`.`resource_schedule_details` ( + `id` bigint unsigned NOT NULL auto_increment, + `schedule_id` bigint unsigned NOT NULL COMMENT 'id of the resource_schedule row', + `name` varchar(255) NOT NULL, + `value` varchar(1024) NOT NULL, + `display` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'should this detail be visible to the end user', + PRIMARY KEY (`id`), + INDEX `i_resource_schedule_details__schedule_id` (`schedule_id`), + CONSTRAINT `fk_resource_schedule_details__schedule_id` FOREIGN KEY (`schedule_id`) REFERENCES `resource_schedule`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- Step 4: rename CRUD event types from VM.SCHEDULE.{CREATE,UPDATE,DELETE} to the new generic SCHEDULE.{CREATE,UPDATE,DELETE}. +-- Action-execution events (VM.SCHEDULE.START, .STOP, .REBOOT, .FORCE_STOP, .FORCE_REBOOT) keep their existing names. +UPDATE `cloud`.`event` SET `type` = 'SCHEDULE.CREATE' WHERE `type` = 'VM.SCHEDULE.CREATE'; +UPDATE `cloud`.`event` SET `type` = 'SCHEDULE.UPDATE' WHERE `type` = 'VM.SCHEDULE.UPDATE'; +UPDATE `cloud`.`event` SET `type` = 'SCHEDULE.DELETE' WHERE `type` = 'VM.SCHEDULE.DELETE'; + +-- Step 5: Rename the global configuration key for the scheduler +UPDATE `cloud`.`configuration` SET name='scheduler.jobs.expire.interval' WHERE name='vmscheduler.jobs.expire.interval'; + +-- Remove stale realhostip.com default values; domain has been dead since ~2015. +UPDATE `cloud`.`configuration` + SET value = NULL + WHERE name IN ('consoleproxy.url.domain', 'secstorage.ssl.cert.domain') + AND value IN ('realhostip.com', '*.realhostip.com'); + +-- Add management_server_details table to allow ManagementServer scope configs +CREATE TABLE IF NOT EXISTS `management_server_details` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', + `management_server_id` bigint unsigned NOT NULL COMMENT 'management server the detail is related to', + `name` varchar(255) NOT NULL COMMENT 'name of the detail', + `value` varchar(255) NOT NULL, + `display` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'True if the detail can be displayed to the end user', + PRIMARY KEY (`id`), + CONSTRAINT `fk_management_server_details__management_server_id` FOREIGN KEY `fk_management_server_details__management_server_id`(`management_server_id`) REFERENCES `mshost`(`id`) ON DELETE CASCADE, + KEY `i_management_server_details__name__value` (`name`(128),`value`(128)) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- Add checkpoint tracking fields to backups table for incremental backup support +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'from_checkpoint_id', 'VARCHAR(255) DEFAULT NULL COMMENT "Previous active checkpoint id for incremental backups"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'to_checkpoint_id', 'VARCHAR(255) DEFAULT NULL COMMENT "New checkpoint id created for the next incremental backup"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'checkpoint_create_time', 'BIGINT DEFAULT NULL COMMENT "Checkpoint creation timestamp from libvirt"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'host_id', 'BIGINT UNSIGNED DEFAULT NULL COMMENT "Host where backup is running"'); + +-- Create image_transfer table for per-disk image transfers +CREATE TABLE IF NOT EXISTS `cloud`.`image_transfer`( + `id` bigint unsigned NOT NULL auto_increment COMMENT 'id', + `uuid` varchar(40) NOT NULL COMMENT 'uuid', + `account_id` bigint unsigned NOT NULL COMMENT 'Account ID', + `domain_id` bigint unsigned NOT NULL COMMENT 'Domain ID', + `data_center_id` bigint unsigned NOT NULL COMMENT 'Data Center ID', + `backup_id` bigint unsigned COMMENT 'Backup ID', + `volume_id` bigint unsigned NOT NULL COMMENT 'Volume ID', + `host_id` bigint unsigned NOT NULL COMMENT 'Host ID', + `transfer_url` varchar(255) COMMENT 'ImageIO transfer URL', + `file` varchar(255) COMMENT 'File for the file backend', + `phase` varchar(20) NOT NULL COMMENT 'Transfer phase: initializing, transferring, finished, failed', + `socket` varchar(255) COMMENT 'Unix socket for nbd backend', + `direction` varchar(20) NOT NULL COMMENT 'Direction: upload, download', + `backend` varchar(20) NOT NULL COMMENT 'Backend: nbd, file', + `progress` int COMMENT 'Transfer progress percentage (0-100)', + `signed_ticket_id` varchar(255) COMMENT 'Signed ticket ID from ImageIO', + `created` datetime NOT NULL COMMENT 'date created', + `updated` datetime COMMENT 'date updated if not null', + `removed` datetime COMMENT 'date removed if not null', + PRIMARY KEY (`id`), + UNIQUE KEY `uuid` (`uuid`), + CONSTRAINT `fk_image_transfer__backup_id` FOREIGN KEY (`backup_id`) REFERENCES `backups`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_image_transfer__volume_id` FOREIGN KEY (`volume_id`) REFERENCES `volumes`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_image_transfer__host_id` FOREIGN KEY (`host_id`) REFERENCES `host`(`id`) ON DELETE CASCADE, + INDEX `i_image_transfer__backup_id`(`backup_id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +--- Quota resource statement +INSERT INTO cloud.role_permissions (uuid, role_id, rule, permission, sort_order) +SELECT uuid(), role_id, 'quotaResourceStatement', permission, sort_order +FROM cloud.role_permissions rp +WHERE rule = 'quotaStatement' AND NOT EXISTS(SELECT 1 FROM cloud.role_permissions rp_ WHERE rp.role_id = rp_.role_id AND rp_.rule = 'quotaResourceStatement'); + +-- Increase length of value of extension details from 255 to 4096 to support longer details value +CALL `cloud`.`IDEMPOTENT_CHANGE_COLUMN`('cloud.extension_details', 'value', 'value', 'VARCHAR(4096)'); +CALL `cloud`.`IDEMPOTENT_CHANGE_COLUMN`('cloud.extension_resource_map_details', 'value', 'value', 'VARCHAR(4096)'); + +-- Add CustomAction service support to physical_network_service_providers +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.physical_network_service_providers', 'custom_action_service_provided', 'tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT "Is Custom Action service provided" AFTER `networkacl_service_provided`'); + +--- Gui theme login base domain +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.gui_themes', 'login_base_domain', 'TEXT DEFAULT NULL'); + +-- Add description for secondary IP addresses +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.nic_secondary_ips', 'description', 'VARCHAR(2048) DEFAULT NULL'); + +--- Change nw_rate and mc_rate column types from smallint unsigned to int unsigned to support larger rate values +ALTER TABLE `cloud`.`service_offering` + MODIFY COLUMN `nw_rate` int unsigned DEFAULT 200 COMMENT 'network rate throttle mbits/s', + MODIFY COLUMN `mc_rate` int unsigned DEFAULT 10 COMMENT 'mcast rate throttle mbits/s'; + +ALTER TABLE `cloud`.`network_offerings` + MODIFY COLUMN `nw_rate` int unsigned COMMENT 'network rate throttle mbits/s', + MODIFY COLUMN `mc_rate` int unsigned COMMENT 'mcast rate throttle mbits/s'; + +-- Soft delete port forwarding, load balancing and firewall rules +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.firewall_rules', 'removed', 'datetime DEFAULT NULL'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.load_balancer_vm_map', 'removed', 'datetime DEFAULT NULL'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.load_balancer_cert_map', 'removed', 'datetime DEFAULT NULL'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.load_balancer_healthcheck_policies', 'removed', 'datetime DEFAULT NULL'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.load_balancer_stickiness_policies', 'removed', 'datetime DEFAULT NULL'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.global_load_balancer_lb_rule_map', 'removed', 'datetime DEFAULT NULL'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.elastic_lb_vm_map', 'removed', 'datetime DEFAULT NULL'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.tungsten_lb_health_monitor', 'removed', 'datetime DEFAULT NULL'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.global_load_balancing_rules', 'removed', 'datetime DEFAULT NULL'); + +ALTER TABLE `cloud`.`load_balancer_vm_map` +DROP KEY `load_balancer_id`, +ADD UNIQUE KEY `load_balancer_id` (`load_balancer_id`, `instance_id`, `instance_ip`, `removed`); + +ALTER TABLE `cloud`.`global_load_balancer_lb_rule_map` +DROP KEY `gslb_rule_id`, +ADD UNIQUE KEY `gslb_rule_id` (`gslb_rule_id`, `lb_rule_id`, `removed`); + +-- ====================================================================== +-- DNS Framework Schema +-- ====================================================================== + +-- DNS Server Table (Stores DNS Server Configurations) +CREATE TABLE IF NOT EXISTS `cloud`.`dns_server` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id of the dns server', + `uuid` varchar(40) COMMENT 'uuid of the dns server', + `name` varchar(255) NOT NULL COMMENT 'display name of the dns server', + `provider_type` varchar(255) NOT NULL COMMENT 'Provider type such as PowerDns', + `url` varchar(1024) NOT NULL COMMENT 'dns server url', + `dns_username` varchar(255) COMMENT 'username or email for dns server credentials', + `dns_api_key` varchar(255) NOT NULL COMMENT 'api key or token for the dns server ', + `port` int(11) DEFAULT NULL COMMENT 'optional dns server port', + `name_servers` varchar(1024) DEFAULT NULL COMMENT 'Comma separated list of name servers', + `is_public` tinyint(1) NOT NULL DEFAULT '0', + `public_domain_suffix` VARCHAR(255), + `state` ENUM('Enabled', 'Disabled') NOT NULL DEFAULT 'Disabled', + `domain_id` bigint unsigned COMMENT 'for domain-specific ownership', + `account_id` bigint(20) unsigned NOT NULL, + `created` datetime NOT NULL COMMENT 'date created', + `removed` datetime DEFAULT NULL COMMENT 'Date removed (soft delete)', + PRIMARY KEY (`id`), + KEY `i_dns_server__account_id` (`account_id`), + CONSTRAINT `fk_dns_server__account_id` FOREIGN KEY (`account_id`) REFERENCES `account` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- DNS Server Details Table +CREATE TABLE IF NOT EXISTS `cloud`.`dns_server_details` ( + `id` bigint unsigned UNIQUE NOT NULL AUTO_INCREMENT COMMENT 'id', + `dns_server_id` bigint unsigned NOT NULL COMMENT 'dns_server 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 'Should detail be displayed to the end user', + PRIMARY KEY (`id`), + CONSTRAINT `fk_dns_server_details__dns_server_id` FOREIGN KEY (`dns_server_id`) REFERENCES `dns_server`(`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- DNS Zone Table (Stores DNS Zone Metadata) +CREATE TABLE IF NOT EXISTS `cloud`.`dns_zone` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id of the dns zone', + `uuid` varchar(40) COMMENT 'uuid of the dns zone', + `name` varchar(255) NOT NULL COMMENT 'dns zone name (e.g. example.com)', + `dns_server_id` bigint unsigned NOT NULL COMMENT 'fk to dns_server.id', + `external_reference` VARCHAR(255) COMMENT 'id of external provider resource', + `domain_id` bigint unsigned COMMENT 'for domain-specific ownership', + `account_id` bigint unsigned COMMENT 'account id. foreign key to account table', + `description` varchar(1024) DEFAULT NULL, + `type` ENUM('Private', 'Public') NOT NULL DEFAULT 'Public', + `state` ENUM('Active', 'Inactive') NOT NULL DEFAULT 'Inactive', + `created` datetime NOT NULL COMMENT 'date created', + `removed` datetime DEFAULT NULL COMMENT 'Date removed (soft delete)', + PRIMARY KEY (`id`), + CONSTRAINT `uc_dns_zone__uuid` UNIQUE (`uuid`), + KEY `i_dns_zone__dns_server` (`dns_server_id`), + KEY `i_dns_zone__account_id` (`account_id`), + CONSTRAINT `fk_dns_zone__dns_server_id` FOREIGN KEY (`dns_server_id`) REFERENCES `dns_server` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_dns_zone__account_id` FOREIGN KEY (`account_id`) REFERENCES `account` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_dns_zone__domain_id` FOREIGN KEY (`domain_id`) REFERENCES `domain` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- DNS Zone Network Map (One-to-Many Link) +CREATE TABLE IF NOT EXISTS `cloud`.`dns_zone_network_map` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id of the dns zone to network mapping', + `uuid` varchar(40), + `dns_zone_id` bigint(20) unsigned NOT NULL, + `network_id` bigint(20) unsigned NOT NULL COMMENT 'network to which dns zone is associated to', + `sub_domain` varchar(255) DEFAULT NULL COMMENT 'Subdomain for auto-registration', + `created` datetime NOT NULL COMMENT 'date created', + `removed` datetime DEFAULT NULL COMMENT 'Date removed (soft delete)', + PRIMARY KEY (`id`), + CONSTRAINT `uc_dns_zone_network_map__uuid` UNIQUE (`uuid`), + KEY `fk_dns_map__zone_id` (`dns_zone_id`), + KEY `fk_dns_map__network_id` (`network_id`), + CONSTRAINT `fk_dns_map__zone_id` FOREIGN KEY (`dns_zone_id`) REFERENCES `dns_zone` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_dns_map__network_id` FOREIGN KEY (`network_id`) REFERENCES `networks` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- This is part of allowing firewall rules on public IP addresses in VPC network +ALTER TABLE `cloud`.`firewall_rules` MODIFY COLUMN `network_id` BIGINT UNSIGNED NULL; + +-- KBOSS + +CREATE TABLE IF NOT EXISTS `cloud`.`internal_backup_pool_ref` ( + `id` bigint NOT NULL UNIQUE AUTO_INCREMENT, + `backup_id` bigint unsigned NOT NULL COMMENT 'The backup ID. Foreign key that points to the backups table.', + `storage_pool_id` bigint unsigned NOT NULL COMMENT 'The storage ID. Foreign key that points to the storage_pool table.', + `volume_id` bigint unsigned NOT NULL COMMENT 'The volumes ID. Foreign key that points to the volumes table.', + `backup_delta_path` varchar(255) COMMENT 'Path of the created delta.', + `backup_parent_path` varchar(255) COMMENT 'Path of the created delta parent.', + PRIMARY KEY (`id`), + CONSTRAINT `fk_internal_backup_pool_ref__backup_id` FOREIGN KEY (`backup_id`) REFERENCES `backups`(`id`), + CONSTRAINT `fk_internal_backup_pool_ref__storage_pool_id` FOREIGN KEY (`storage_pool_id`) REFERENCES `storage_pool`(`id`), + CONSTRAINT `fk_internal_backup_pool_ref__volume_id` FOREIGN KEY (`volume_id`) REFERENCES `volumes`(`id`) + ); + +CREATE TABLE IF NOT EXISTS `cloud`.`internal_backup_store_ref` ( + `id` bigint NOT NULL UNIQUE AUTO_INCREMENT, + `backup_id` bigint unsigned NOT NULL COMMENT 'The backup ID. Foreign key that points to the backups table.', + `volume_id` bigint unsigned NOT NULL COMMENT 'The volume ID. Foreign key that points to the volumes table.', + `device_id` bigint unsigned COMMENT 'device ID of the volume', + `path` varchar(255) COMMENT 'Path of the backup.', + PRIMARY KEY (`id`), + CONSTRAINT `fk_internal_backup_store_ref__backup_id` FOREIGN KEY (`backup_id`) REFERENCES `backups`(`id`), + CONSTRAINT `fk_internal_backup_store_ref__volume_id` FOREIGN KEY (`volume_id`) REFERENCES `volumes`(`id`) + ); + +CREATE TABLE IF NOT EXISTS `cloud`.`internal_backup_service_job` ( + `id` bigint NOT NULL UNIQUE AUTO_INCREMENT, + `backup_id` bigint unsigned NOT NULL COMMENT 'The backup ID. Foreign key that points to the backups table.', + `instance_id` bigint unsigned NOT NULL COMMENT 'The instance ID. Foreign key that points to the vm_instance table.', + `account_id` bigint(20) unsigned COMMENT 'Account ID of the owner of the VM.', + `host_id` bigint unsigned COMMENT 'The host ID that is executing the compression. Foreign key that points to the host table.', + `zone_id` bigint unsigned NOT NULL COMMENT 'The zone ID of the where the VM is. Foreign key that points to the data_center table', + `attempts` int(32) unsigned NOT NULL DEFAULT 0, + `type` varchar(55) NOT NULL, + `created` datetime NOT NULL, + `scheduled_start_time` datetime NOT NULL, + `start_time` datetime, + `removed` datetime, + PRIMARY KEY (`id`), + CONSTRAINT `fk_internal_backup_service_job__backup_id` FOREIGN KEY (`backup_id`) REFERENCES `backups`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_internal_backup_service_job__instance_id` FOREIGN KEY (`instance_id`) REFERENCES `vm_instance`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_internal_backup_service_job__host_id` FOREIGN KEY (`host_id`) REFERENCES `host`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_internal_backup_service_job__zone_id` FOREIGN KEY (`zone_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_internal_backup_service_job__account_id` FOREIGN KEY (`account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE + ); + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'uncompressed_size', 'bigint unsigned'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'compression_status', 'varchar(55)'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'validation_status', 'varchar(55)'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backup_schedule', 'isolated', 'TINYINT(1) NOT NULL DEFAULT 0 COMMENT "Whether the scheduled backups will be isolated or not."'); + +UPDATE `cloud`.`configuration` SET `value`=CONCAT(`value`, ', backupValidationCommandTimeout, backupValidationScreenshotWait, backupValidationBootTimeout') +WHERE `name`='user.vm.readonly.details' AND `value` IS NOT NULL; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.dns_server_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.dns_server_view.sql new file mode 100644 index 000000000000..a1bc1a1141a9 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.dns_server_view.sql @@ -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. + +-- VIEW `cloud`.`dns_server_view`; + +DROP VIEW IF EXISTS `cloud`.`dns_server_view`; +CREATE VIEW `cloud`.`dns_server_view` AS + SELECT + dns.id, + dns.uuid, + dns.name, + dns.provider_type, + dns.url, + dns.port, + dns.name_servers, + dns.is_public, + dns.public_domain_suffix, + dns.state, + dns.created, + dns.removed, + account.account_name account_name, + domain.name domain_name, + domain.uuid domain_uuid, + domain.path domain_path + FROM + `cloud`.`dns_server` dns + INNER JOIN + `cloud`.`account` account ON dns.account_id = account.id + INNER JOIN + `cloud`.`domain` domain ON dns.domain_id = domain.id; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.dns_zone_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.dns_zone_view.sql new file mode 100644 index 000000000000..a41e003ae4f2 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.dns_zone_view.sql @@ -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. + +-- VIEW `cloud`.`dns_zone_view`; + +DROP VIEW IF EXISTS `cloud`.`dns_zone_view`; +CREATE VIEW `cloud`.`dns_zone_view` AS + SELECT + zone.id, + zone.uuid, + zone.name, + zone.dns_server_id, + zone.state, + zone.description, + server.uuid dns_server_uuid, + server.name dns_server_name, + server_account.account_name dns_server_account_name, + account.account_name account_name, + domain.name domain_name, + domain.uuid domain_uuid, + domain.path domain_path + FROM + `cloud`.`dns_zone` zone + INNER JOIN + `cloud`.`dns_server` server ON zone.dns_server_id = server.id + INNER JOIN + `cloud`.`account` server_account ON server.account_id = server_account.id + INNER JOIN + `cloud`.`account` account ON zone.account_id = account.id + INNER JOIN + `cloud`.`domain` domain ON zone.domain_id = domain.id; 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 a12e02bcfdb8..d5f17606cb41 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 @@ -76,6 +76,7 @@ select nics.broadcast_uri broadcast_uri, nics.isolation_uri isolation_uri, nics.mtu mtu, + nics.enabled is_nic_enabled, vpc.id vpc_id, vpc.uuid vpc_uuid, vpc.name vpc_name, 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 index 3173274623ed..5f49cffbf1f5 100644 --- 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 @@ -27,6 +27,7 @@ SELECT `cloud`.`gui_themes`.`description` AS `description`, `cloud`.`gui_themes`.`css` AS `css`, `cloud`.`gui_themes`.`json_configuration` AS `json_configuration`, + `cloud`.`gui_themes`.`login_base_domain` AS `login_base_domain`, (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, 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 d9f4e2671595..255ce866c427 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 @@ -64,6 +64,7 @@ SELECT guest_os_category.id guest_os_category_id, guest_os_category.uuid guest_os_category_uuid, guest_os_category.name guest_os_category_name, + (SELECT `value` FROM `cloud`.`host_details` `hd` WHERE `hd`.`host_id` = `cloud`.`host`.`id` AND `hd`.`name` = 'guest.os.rule') AS `guest_os_rule`, mem_caps.used_capacity memory_used_capacity, mem_caps.reserved_capacity memory_reserved_capacity, cpu_caps.used_capacity cpu_used_capacity, diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.internal_backup_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.internal_backup_view.sql new file mode 100644 index 000000000000..a1fbd102630b --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.internal_backup_view.sql @@ -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. + +-- VIEW `cloud`.`internal_backup_view`; + +DROP VIEW IF EXISTS `cloud`.`internal_backup_view`; +CREATE VIEW `cloud`.`internal_backup_view` AS +SELECT b.id, + b.uuid, + b.vm_id, + b.backed_volumes, + b.type, + b.date, + b.status, + b.compression_status, + b.backup_offering_id, + b.size, + b.protected_size, + b.zone_id, + MAX(CASE WHEN bd.name = 'image_store_id' THEN bd.value END) image_store_id, + MAX(CASE WHEN bd.name = 'parent_id' THEN bd.value END) parent_id, + MAX(CASE WHEN bd.name = 'end_of_chain' THEN bd.value END) end_of_chain, + MAX(CASE WHEN bd.name = 'current' THEN bd.value END) current, + COALESCE(MAX(CASE WHEN bd.name = 'isolated' THEN bd.value END), 'false') isolated, + nbpr.volume_id, + nbsr.path image_store_path +FROM backups b +LEFT JOIN backup_details bd ON b.id = bd.backup_id +LEFT JOIN backup_offering bo ON b.backup_offering_id = bo.id +LEFT JOIN internal_backup_store_ref nbsr ON b.id = nbsr.backup_id +LEFT JOIN internal_backup_pool_ref nbpr ON nbpr.volume_id = nbsr.volume_id +WHERE bo.provider='kboss' +GROUP BY b.id, nbsr.volume_id; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.user_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.user_view.sql index 340cfa9055fb..dcba71e10985 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.user_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.user_view.sql @@ -29,8 +29,6 @@ select user.lastname, user.email, user.state, - user.api_key, - user.secret_key, user.created, user.removed, user.timezone, 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 94bc8640fd54..fbf126608eae 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 @@ -56,6 +56,7 @@ SELECT `vm_instance`.`display_vm` AS `display_vm`, `vm_instance`.`delete_protection` AS `delete_protection`, `guest_os`.`uuid` AS `guest_os_uuid`, + `guest_os`.`display_name` AS `guest_os_display_name`, `vm_instance`.`pod_id` AS `pod_id`, `host_pod_ref`.`uuid` AS `pod_uuid`, `vm_instance`.`private_ip_address` AS `private_ip_address`, @@ -79,6 +80,7 @@ SELECT `vm_template`.`format` AS `template_format`, `vm_template`.`display_text` AS `template_display_text`, `vm_template`.`enable_password` AS `password_enabled`, + `vm_template`.`extension_id` AS `template_extension_id`, `iso`.`id` AS `iso_id`, `iso`.`uuid` AS `iso_uuid`, `iso`.`name` AS `iso_name`, @@ -141,6 +143,8 @@ SELECT `nics`.`mac_address` AS `mac_address`, `nics`.`broadcast_uri` AS `broadcast_uri`, `nics`.`isolation_uri` AS `isolation_uri`, + `nics`.`enabled` AS `is_nic_enabled`, + `nic_details`.`value` AS `nic_dns_name`, `vpc`.`id` AS `vpc_id`, `vpc`.`uuid` AS `vpc_uuid`, `networks`.`uuid` AS `network_uuid`, @@ -185,7 +189,7 @@ SELECT `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`))) @@ -212,6 +216,7 @@ FROM LEFT JOIN `user_data` ON ((`user_data`.`id` = `user_vm`.`user_data_id`))) LEFT JOIN `nics` ON (((`vm_instance`.`id` = `nics`.`instance_id`) AND ISNULL(`nics`.`removed`)))) + LEFT JOIN `nic_details` ON ((`nic_details`.`nic_id` = `nics`.`id`) AND (`nic_details`.`name` = 'nicdnsname'))) LEFT JOIN `networks` ON ((`nics`.`network_id` = `networks`.`id`))) LEFT JOIN `vpc` ON (((`networks`.`vpc_id` = `vpc`.`id`) AND ISNULL(`vpc`.`removed`)))) diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.volume_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.volume_view.sql index ffeb93e8fa7a..8ba7e5c6df77 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.volume_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.volume_view.sql @@ -40,6 +40,10 @@ SELECT `volumes`.`chain_info` AS `chain_info`, `volumes`.`external_uuid` AS `external_uuid`, `volumes`.`encrypt_format` AS `encrypt_format`, + `volumes`.`kms_key_id` AS `kms_key_id`, + `kms_keys`.`uuid` AS `kms_key_uuid`, + `kms_keys`.`name` AS `kms_key_name`, + `volumes`.`kms_wrapped_key_id` AS `kms_wrapped_key_id`, `volumes`.`delete_protection` AS `delete_protection`, `account`.`id` AS `account_id`, `account`.`uuid` AS `account_uuid`, @@ -116,7 +120,7 @@ SELECT `resource_tag_domain`.`uuid` AS `tag_domain_uuid`, `resource_tag_domain`.`name` AS `tag_domain_name` FROM - ((((((((((((((((((`volumes` + (((((((((((((((((((`volumes` JOIN `account`ON ((`volumes`.`account_id` = `account`.`id`))) JOIN `domain`ON @@ -129,8 +133,10 @@ LEFT JOIN `vm_instance`ON ((`volumes`.`instance_id` = `vm_instance`.`id`))) LEFT JOIN `user_vm`ON ((`user_vm`.`id` = `vm_instance`.`id`))) -LEFT JOIN `volume_store_ref`ON +LEFT JOIN `volume_store_ref` ON ((`volumes`.`id` = `volume_store_ref`.`volume_id`))) +LEFT JOIN `kms_keys` ON + ((`volumes`.`kms_key_id` = `kms_keys`.`id`))) LEFT JOIN `service_offering`ON ((`vm_instance`.`service_offering_id` = `service_offering`.`id`))) LEFT JOIN `disk_offering`ON 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 751d8f91a259..3669bb10122b 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 @@ -38,6 +38,7 @@ select `vpc_offerings`.`sort_key` AS `sort_key`, `vpc_offerings`.`routing_mode` AS `routing_mode`, `vpc_offerings`.`specify_as_number` AS `specify_as_number`, + `vpc_offerings`.`conserve_mode` AS `conserve_mode`, group_concat(distinct `domain`.`id` separator ',') AS `domain_id`, group_concat(distinct `domain`.`uuid` separator ',') AS `domain_uuid`, group_concat(distinct `domain`.`name` separator ',') AS `domain_name`, diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud_usage.quota_summary_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud_usage.quota_summary_view.sql new file mode 100644 index 000000000000..0646c3b6f919 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud_usage.quota_summary_view.sql @@ -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. + +-- cloud_usage.quota_summary_view source + +-- Create view for quota summary +DROP VIEW IF EXISTS `cloud_usage`.`quota_summary_view`; +CREATE VIEW `cloud_usage`.`quota_summary_view` AS +SELECT + cloud_usage.quota_account.account_id AS account_id, + cloud_usage.quota_account.quota_balance AS quota_balance, + cloud_usage.quota_account.quota_balance_date AS quota_balance_date, + cloud_usage.quota_account.quota_enforce AS quota_enforce, + cloud_usage.quota_account.quota_min_balance AS quota_min_balance, + cloud_usage.quota_account.quota_alert_date AS quota_alert_date, + cloud_usage.quota_account.quota_alert_type AS quota_alert_type, + cloud_usage.quota_account.last_statement_date AS last_statement_date, + cloud.account.uuid AS account_uuid, + cloud.account.account_name AS account_name, + cloud.account.state AS account_state, + cloud.account.removed AS account_removed, + cloud.domain.id AS domain_id, + cloud.domain.uuid AS domain_uuid, + cloud.domain.name AS domain_name, + cloud.domain.path AS domain_path, + cloud.domain.removed AS domain_removed, + cloud.projects.uuid AS project_uuid, + cloud.projects.name AS project_name, + cloud.projects.removed AS project_removed +FROM + cloud_usage.quota_account + INNER JOIN cloud.account ON (cloud.account.id = cloud_usage.quota_account.account_id) + INNER JOIN cloud.domain ON (cloud.domain.id = cloud.account.domain_id) + LEFT JOIN cloud.projects ON (cloud.account.type = 5 AND cloud.account.id = cloud.projects.project_account_id); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud_usage.quota_usage_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud_usage.quota_usage_view.sql new file mode 100644 index 000000000000..7ac001384e8e --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud_usage.quota_usage_view.sql @@ -0,0 +1,35 @@ +-- 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_usage`.`quota_usage_view`; + +DROP VIEW IF EXISTS `cloud_usage`.`quota_usage_view`; +CREATE VIEW `cloud_usage`.`quota_usage_view` AS +SELECT qu.id, + qu.usage_item_id, + qu.zone_id, + qu.account_id, + qu.domain_id, + qu.usage_type, + qu.quota_used, + qu.start_date, + qu.end_date, + cu.usage_id AS resource_id, + cu.network_id as network_id, + cu.offering_id as offering_id +FROM `cloud_usage`.`quota_usage` qu +INNER JOIN `cloud_usage`.`cloud_usage` cu ON (cu.id = qu.usage_item_id); diff --git a/engine/schema/src/main/resources/META-INF/db/views/nic_dns_view.sql b/engine/schema/src/main/resources/META-INF/db/views/nic_dns_view.sql new file mode 100644 index 000000000000..8c7737db0884 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/nic_dns_view.sql @@ -0,0 +1,40 @@ +-- 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`.`nic_dns_view`; + +DROP VIEW IF EXISTS `cloud`.`nic_dns_view`; +CREATE VIEW `cloud`.`nic_dns_view` AS +SELECT + n.id AS id, + n.uuid AS uuid, + n.instance_id AS instance_id, + n.network_id AS network_id, + n.ip4_address AS ip4_address, + n.ip6_address AS ip6_address, + n.removed AS removed, + nd.value AS nic_dns_name, + map.dns_zone_id AS dns_zone_id, + map.sub_domain AS sub_domain +FROM + `cloud`.`nics` n + INNER JOIN + `cloud`.`dns_zone_network_map` map ON n.network_id = map.network_id + LEFT JOIN + `cloud`.`nic_details` nd ON n.id = nd.nic_id AND nd.name = 'nicdnsname' +WHERE + n.instance_id IS NOT NULL AND map.removed IS NULL; 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 000000000000..8f84ecac3dda --- /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/network/dao/NetworkDaoImplTest.java b/engine/schema/src/test/java/com/cloud/network/dao/NetworkDaoImplTest.java index ab5f43521052..a78eab568af0 100644 --- a/engine/schema/src/test/java/com/cloud/network/dao/NetworkDaoImplTest.java +++ b/engine/schema/src/test/java/com/cloud/network/dao/NetworkDaoImplTest.java @@ -22,7 +22,6 @@ import com.cloud.network.Networks; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; -import com.cloud.utils.db.TransactionLegacy; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -30,7 +29,6 @@ import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; - import java.util.List; @RunWith(MockitoJUnitRunner.class) @@ -46,26 +44,21 @@ public class NetworkDaoImplTest { List listNetworkVoMock; @Test - public void listByPhysicalNetworkTrafficTypeTestSetParametersValidation() throws Exception { + public void listByPhysicalNetworkTrafficTypeTestSetParametersValidation() { NetworkDaoImpl networkDaoImplSpy = Mockito.spy(NetworkDaoImpl.class); - TransactionLegacy txn = TransactionLegacy.open("runNetworkDaoImplTest"); - try { - networkDaoImplSpy.AllFieldsSearch = searchBuilderNetworkVoMock; - Mockito.doReturn(searchCriteriaNetworkVoMock).when(searchBuilderNetworkVoMock).create(); - Mockito.doNothing().when(searchCriteriaNetworkVoMock).setParameters(Mockito.anyString(), Mockito.any()); - Mockito.doReturn(listNetworkVoMock).when(networkDaoImplSpy).listBy(Mockito.any(SearchCriteria.class)); - - long expectedPhysicalNetwork = 2513l; + networkDaoImplSpy.AllFieldsSearch = searchBuilderNetworkVoMock; + Mockito.doReturn(searchCriteriaNetworkVoMock).when(searchBuilderNetworkVoMock).create(); + Mockito.doNothing().when(searchCriteriaNetworkVoMock).setParameters(Mockito.anyString(), Mockito.any()); + Mockito.doReturn(listNetworkVoMock).when(networkDaoImplSpy).listBy(Mockito.any(SearchCriteria.class)); - for (Networks.TrafficType trafficType : Networks.TrafficType.values()) { - List result = networkDaoImplSpy.listByPhysicalNetworkTrafficType(expectedPhysicalNetwork, trafficType); - Assert.assertEquals(listNetworkVoMock, result); - Mockito.verify(searchCriteriaNetworkVoMock).setParameters("trafficType", trafficType); - } + long expectedPhysicalNetwork = 2513l; - Mockito.verify(searchCriteriaNetworkVoMock, Mockito.times(Networks.TrafficType.values().length)).setParameters("physicalNetwork", expectedPhysicalNetwork); - } finally { - txn.close(); + for (Networks.TrafficType trafficType : Networks.TrafficType.values()) { + List result = networkDaoImplSpy.listByPhysicalNetworkTrafficType(expectedPhysicalNetwork, trafficType); + Assert.assertEquals(listNetworkVoMock, result); + Mockito.verify(searchCriteriaNetworkVoMock).setParameters("trafficType", trafficType); } + + Mockito.verify(searchCriteriaNetworkVoMock, Mockito.times(Networks.TrafficType.values().length)).setParameters("physicalNetworkId", expectedPhysicalNetwork); } } 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 index 3c8e4c046ae7..5cff77869be8 100644 --- a/engine/schema/src/test/java/com/cloud/storage/dao/VMTemplateDaoImplTest.java +++ b/engine/schema/src/test/java/com/cloud/storage/dao/VMTemplateDaoImplTest.java @@ -76,7 +76,8 @@ 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()); + VMTemplateVO result = templateDao.findLatestTemplateByName("test", Hypervisor.HypervisorType.KVM, + CPU.CPUArch.getDefault()); assertNotNull("Expected a non-null template", result); assertEquals("Expected the returned template to be the first element", expectedTemplate, result); } @@ -85,7 +86,8 @@ public void testFindLatestTemplateByName_ReturnsTemplate() { 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()); + VMTemplateVO result = templateDao.findLatestTemplateByName("test", Hypervisor.HypervisorType.VMware, + CPU.CPUArch.getDefault()); assertNull("Expected null when no templates are found", result); } @@ -94,7 +96,8 @@ 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); + VMTemplateVO result = templateDao.findLatestTemplateByName("test", Hypervisor.HypervisorType.XenServer, + 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); } @@ -337,4 +340,82 @@ public void testFindSystemVMReadyTemplate() { VMTemplateVO readyTemplate = templateDao.findSystemVMReadyTemplate(zoneId, Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64.getType()); Assert.assertEquals(CPU.CPUArch.arm64, readyTemplate.getArch()); } + + @Test + public void findActiveSystemTemplateByHypervisorArchAndUrlPath_ReturnsTemplate() { + VMTemplateVO expectedTemplate = mock(VMTemplateVO.class); + SearchBuilder sb = mock(SearchBuilder.class); + when(sb.entity()).thenReturn(expectedTemplate); + SearchCriteriasc = mock(SearchCriteria.class); + when(sb.create()).thenReturn(sc); + when(templateDao.createSearchBuilder()).thenReturn(sb); + List templates = Collections.singletonList(expectedTemplate); + doReturn(templates).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class)); + + VMTemplateVO result = templateDao.findActiveSystemTemplateByHypervisorArchAndUrlPath( + Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64, "testPath"); + + assertNotNull(result); + assertEquals(expectedTemplate, result); + } + + @Test + public void findActiveSystemTemplateByHypervisorArchAndUrlPath_ReturnsNullWhenNoTemplatesFound() { + VMTemplateVO template = mock(VMTemplateVO.class); + SearchBuilder sb = mock(SearchBuilder.class); + when(sb.entity()).thenReturn(template); + SearchCriteriasc = mock(SearchCriteria.class); + when(sb.create()).thenReturn(sc); + when(templateDao.createSearchBuilder()).thenReturn(sb); + doReturn(Collections.emptyList()).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class)); + + VMTemplateVO result = templateDao.findActiveSystemTemplateByHypervisorArchAndUrlPath( + Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64, "testPath"); + + assertNull(result); + } + + @Test + public void findActiveSystemTemplateByHypervisorArchAndUrlPath_NullHypervisor() { + VMTemplateVO expectedTemplate = mock(VMTemplateVO.class); + SearchBuilder sb = mock(SearchBuilder.class); + when(sb.entity()).thenReturn(expectedTemplate); + SearchCriteriasc = mock(SearchCriteria.class); + when(sb.create()).thenReturn(sc); + when(templateDao.createSearchBuilder()).thenReturn(sb); + List templates = Collections.singletonList(expectedTemplate); + doReturn(templates).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class)); + + VMTemplateVO result = templateDao.findActiveSystemTemplateByHypervisorArchAndUrlPath( + null, CPU.CPUArch.amd64, "testPath"); + + assertNotNull(result); + assertEquals(expectedTemplate, result); + } + + @Test + public void findActiveSystemTemplateByHypervisorArchAndUrlPath_NullArch() { + VMTemplateVO expectedTemplate = mock(VMTemplateVO.class); + SearchBuilder sb = mock(SearchBuilder.class); + when(sb.entity()).thenReturn(expectedTemplate); + SearchCriteriasc = mock(SearchCriteria.class); + when(sb.create()).thenReturn(sc); + when(templateDao.createSearchBuilder()).thenReturn(sb); + List templates = Collections.singletonList(expectedTemplate); + doReturn(templates).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class)); + + VMTemplateVO result = templateDao.findActiveSystemTemplateByHypervisorArchAndUrlPath( + Hypervisor.HypervisorType.KVM, null, "testPath"); + + assertNotNull(result); + assertEquals(expectedTemplate, result); + } + + @Test + public void findActiveSystemTemplateByHypervisorArchAndUrlPath_EmptyUrlPathSuffix() { + VMTemplateVO result = templateDao.findActiveSystemTemplateByHypervisorArchAndUrlPath( + Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64, ""); + + assertNull(result); + } } diff --git a/engine/schema/src/test/java/com/cloud/storage/dao/VolumeDaoImplTest.java b/engine/schema/src/test/java/com/cloud/storage/dao/VolumeDaoImplTest.java index 9445efeb089c..6f153727ab76 100644 --- a/engine/schema/src/test/java/com/cloud/storage/dao/VolumeDaoImplTest.java +++ b/engine/schema/src/test/java/com/cloud/storage/dao/VolumeDaoImplTest.java @@ -41,6 +41,7 @@ import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; +import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; import com.cloud.utils.db.Filter; import com.cloud.utils.db.SearchBuilder; @@ -113,6 +114,69 @@ public void testListPoolIdsByVolumeCount_without_cluster_details() throws SQLExc verify(preparedStatementMock, times(1)).executeQuery(); } + @Test + public void findByInstanceAndNotState_queriesWithInstanceIdAndExcludedStates() { + SearchBuilder sb = Mockito.mock(SearchBuilder.class); + SearchCriteria sc = Mockito.mock(SearchCriteria.class); + Mockito.when(sb.create()).thenReturn(sc); + Mockito.doReturn(new ArrayList<>()).when(volumeDao).listBy(sc); + Mockito.when(volumeDao.createSearchBuilder()).thenReturn(sb); + VolumeVO mockedVO = Mockito.mock(VolumeVO.class); + Mockito.when(sb.entity()).thenReturn(mockedVO); + + volumeDao.findByInstanceAndNotStates(42L, Volume.State.Ready); + + Mockito.verify(sc).setParameters("instanceId", 42L); + Mockito.verify(sc).setParameters("state", (Object[]) new Volume.State[]{Volume.State.Ready}); + } + + @Test + public void findByInstanceAndNotStates_withMultipleExcludedStates_passesAllStatesToCriteria() { + SearchBuilder sb = Mockito.mock(SearchBuilder.class); + SearchCriteria sc = Mockito.mock(SearchCriteria.class); + Mockito.when(sb.create()).thenReturn(sc); + Mockito.doReturn(new ArrayList<>()).when(volumeDao).listBy(sc); + Mockito.when(volumeDao.createSearchBuilder()).thenReturn(sb); + VolumeVO mockedVO = Mockito.mock(VolumeVO.class); + Mockito.when(sb.entity()).thenReturn(mockedVO); + + volumeDao.findByInstanceAndNotStates(7L, Volume.State.Destroy, Volume.State.Expunged); + + Mockito.verify(sc).setParameters("instanceId", 7L); + Mockito.verify(sc).setParameters("state", + (Object[]) new Volume.State[]{Volume.State.Destroy, Volume.State.Expunged}); + } + + @Test + public void findByInstanceAndNotStates_returnsResultFromDao() { + SearchBuilder sb = Mockito.mock(SearchBuilder.class); + SearchCriteria sc = Mockito.mock(SearchCriteria.class); + Mockito.when(sb.create()).thenReturn(sc); + VolumeVO vol = Mockito.mock(VolumeVO.class); + Mockito.doReturn(List.of(vol)).when(volumeDao).listBy(sc); + Mockito.when(volumeDao.createSearchBuilder()).thenReturn(sb); + Mockito.when(sb.entity()).thenReturn(Mockito.mock(VolumeVO.class)); + + List result = volumeDao.findByInstanceAndNotStates(1L, Volume.State.Ready); + + Assert.assertEquals(1, result.size()); + Assert.assertSame(vol, result.get(0)); + } + + @Test + public void findByInstanceAndNotStates_noMatchingVolumes_returnsEmptyList() { + SearchBuilder sb = Mockito.mock(SearchBuilder.class); + SearchCriteria sc = Mockito.mock(SearchCriteria.class); + Mockito.when(sb.create()).thenReturn(sc); + Mockito.doReturn(new ArrayList<>()).when(volumeDao).listBy(sc); + Mockito.when(volumeDao.createSearchBuilder()).thenReturn(sb); + Mockito.when(sb.entity()).thenReturn(Mockito.mock(VolumeVO.class)); + + List result = volumeDao.findByInstanceAndNotStates(99L, Volume.State.Ready); + + Assert.assertTrue(result.isEmpty()); + } + @Test public void testSearchRemovedByVmsNoVms() { Assert.assertTrue(CollectionUtils.isEmpty(volumeDao.searchRemovedByVms( @@ -141,5 +205,4 @@ public void testSearchRemovedByVms() { Mockito.any(SearchCriteria.class), Mockito.any(Filter.class), Mockito.eq(null), Mockito.eq(false)); } - } diff --git a/engine/schema/src/test/java/com/cloud/upgrade/DatabaseUpgradeCheckerDoUpgradesTest.java b/engine/schema/src/test/java/com/cloud/upgrade/DatabaseUpgradeCheckerDoUpgradesTest.java new file mode 100644 index 000000000000..6241bd7cede0 --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/upgrade/DatabaseUpgradeCheckerDoUpgradesTest.java @@ -0,0 +1,173 @@ +// 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.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.cloud.upgrade.dao.VersionDao; +import com.cloud.upgrade.dao.VersionDaoImpl; +import com.cloud.upgrade.dao.VersionVO; +import com.cloud.utils.db.GlobalLock; +import org.junit.Test; + +public class DatabaseUpgradeCheckerDoUpgradesTest { + + static class StubVersionDao extends VersionDaoImpl implements VersionDao { + private final String currentVersion; + + StubVersionDao(String currentVersion) { + this.currentVersion = currentVersion; + } + + @Override + public VersionVO findByVersion(String version, VersionVO.Step step) { + return null; + } + + @Override + public String getCurrentVersion() { + return currentVersion; + } + + } + + private static class TestableChecker extends DatabaseUpgradeChecker { + boolean initializeCalled = false; + boolean upgradeCalled = false; + boolean clusterHandlerCalled = false; + String implVersionOverride = null; + String sysVmMetadataOverride = "4.8.0"; + boolean standaloneOverride = true; + + TestableChecker(String daoVersion) { + // set a stub DAO + this._dao = new StubVersionDao(daoVersion); + } + + @Override + protected void initializeDatabaseEncryptors() { + initializeCalled = true; + // noop instead of doing DB work + } + + @Override + protected String getImplementationVersion() { + return implVersionOverride; + } + + @Override + protected String parseSystemVmMetadata() { + return sysVmMetadataOverride; + } + + @Override + boolean isStandalone() { + return standaloneOverride; + } + + @Override + protected void upgrade(org.apache.cloudstack.utils.CloudStackVersion dbVersion, org.apache.cloudstack.utils.CloudStackVersion currentVersion) { + upgradeCalled = true; + } + + @Override + protected void handleClusteredUpgradeRequired() { + clusterHandlerCalled = true; + } + } + + @Test + public void testDoUpgrades_noImplementationVersion_returnsEarly() { + TestableChecker checker = new TestableChecker("4.8.0"); + checker.implVersionOverride = ""; // blank -> should return early + + GlobalLock lock = GlobalLock.getInternLock("test-noimpl"); + try { + // acquire lock so doUpgrades can safely call unlock in finally + lock.lock(1); + checker.doUpgrades(lock); + } finally { + // ensure lock released if still held + lock.releaseRef(); + } + + assertTrue("initializeDatabaseEncryptors should be called before returning", checker.initializeCalled); + assertFalse("upgrade should not be called when implementation version is blank", checker.upgradeCalled); + assertFalse("cluster handler should not be called", checker.clusterHandlerCalled); + } + + @Test + public void testDoUpgrades_dbUpToDate_noUpgrade() { + // DB version = code version -> no upgrade + TestableChecker checker = new TestableChecker("4.8.1"); + checker.implVersionOverride = "4.8.1"; + checker.sysVmMetadataOverride = "4.8.1"; + + GlobalLock lock = GlobalLock.getInternLock("test-uptodate"); + try { + lock.lock(1); + checker.doUpgrades(lock); + } finally { + lock.releaseRef(); + } + + assertTrue(checker.initializeCalled); + assertFalse(checker.upgradeCalled); + assertFalse(checker.clusterHandlerCalled); + } + + @Test + public void testDoUpgrades_requiresUpgrade_standalone_invokesUpgrade() { + TestableChecker checker = new TestableChecker("4.8.0"); + checker.implVersionOverride = "4.8.2"; // code is newer than DB + checker.sysVmMetadataOverride = "4.8.2"; + checker.standaloneOverride = true; + + GlobalLock lock = GlobalLock.getInternLock("test-upgrade-standalone"); + try { + lock.lock(1); + checker.doUpgrades(lock); + } finally { + lock.releaseRef(); + } + + assertTrue(checker.initializeCalled); + assertTrue("upgrade should be invoked in standalone mode", checker.upgradeCalled); + assertFalse(checker.clusterHandlerCalled); + } + + @Test + public void testDoUpgrades_requiresUpgrade_clustered_invokesHandler() { + TestableChecker checker = new TestableChecker("4.8.0"); + checker.implVersionOverride = "4.8.2"; // code is newer than DB + checker.sysVmMetadataOverride = "4.8.2"; + checker.standaloneOverride = false; + + GlobalLock lock = GlobalLock.getInternLock("test-upgrade-clustered"); + try { + lock.lock(1); + checker.doUpgrades(lock); + } finally { + lock.releaseRef(); + } + + assertTrue(checker.initializeCalled); + assertFalse("upgrade should not be invoked in clustered mode", checker.upgradeCalled); + assertTrue("cluster handler should be invoked in clustered mode", checker.clusterHandlerCalled); + } +} diff --git a/engine/schema/src/test/java/com/cloud/upgrade/DatabaseUpgradeCheckerTest.java b/engine/schema/src/test/java/com/cloud/upgrade/DatabaseUpgradeCheckerTest.java index 1a9372f73eaf..884398cf410d 100644 --- a/engine/schema/src/test/java/com/cloud/upgrade/DatabaseUpgradeCheckerTest.java +++ b/engine/schema/src/test/java/com/cloud/upgrade/DatabaseUpgradeCheckerTest.java @@ -16,14 +16,24 @@ // under the License. package com.cloud.upgrade; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import java.sql.SQLException; +import java.lang.reflect.Field; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; -import java.util.Arrays; +import javax.sql.DataSource; import org.apache.cloudstack.utils.CloudStackVersion; import org.junit.Test; +import org.junit.Before; +import org.junit.After; +import org.junit.runner.RunWith; + +import org.mockito.ArgumentMatchers; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; import com.cloud.upgrade.DatabaseUpgradeChecker.NoopDbUpgrade; import com.cloud.upgrade.dao.DbUpgrade; @@ -34,6 +44,9 @@ import com.cloud.upgrade.dao.Upgrade41120to41200; import com.cloud.upgrade.dao.Upgrade41510to41520; import com.cloud.upgrade.dao.Upgrade41610to41700; +import com.cloud.upgrade.dao.Upgrade42020to42030; +import com.cloud.upgrade.dao.Upgrade42030to42040; +import com.cloud.upgrade.dao.Upgrade42040to42100; import com.cloud.upgrade.dao.Upgrade452to453; import com.cloud.upgrade.dao.Upgrade453to460; import com.cloud.upgrade.dao.Upgrade460to461; @@ -43,8 +56,51 @@ import com.cloud.upgrade.dao.Upgrade480to481; import com.cloud.upgrade.dao.Upgrade490to4910; +import com.cloud.utils.db.TransactionLegacy; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertArrayEquals; + + +@RunWith(MockitoJUnitRunner.class) public class DatabaseUpgradeCheckerTest { + @Mock + DataSource dataSource; + + @Mock + Connection connection; + + @Mock + PreparedStatement preparedStatement; + + @Mock + ResultSet resultSet; + + private DataSource backupDataSource; + + @Before + public void setup() throws Exception { + Field dsField = TransactionLegacy.class.getDeclaredField("s_ds"); + dsField.setAccessible(true); + backupDataSource = (DataSource) dsField.get(null); + dsField.set(null, dataSource); + + Mockito.when(dataSource.getConnection()).thenReturn(connection); + Mockito.when(connection.prepareStatement(ArgumentMatchers.anyString())).thenReturn(preparedStatement); + Mockito.when(preparedStatement.executeQuery()).thenReturn(resultSet); + } + + @After + public void cleanup() throws Exception { + Field dsField = TransactionLegacy.class.getDeclaredField("s_ds"); + dsField.setAccessible(true); + dsField.set(null, backupDataSource); + } + @Test public void testCalculateUpgradePath480to481() { @@ -79,7 +135,7 @@ public void testCalculateUpgradePath490to4910() { assertTrue(upgrades.length >= 1); assertTrue(upgrades[0] instanceof Upgrade490to4910); - assertTrue(Arrays.equals(new String[] {"4.9.0", currentVersion.toString()}, upgrades[0].getUpgradableVersionRange())); + assertArrayEquals(new String[]{"4.9.0", currentVersion.toString()}, upgrades[0].getUpgradableVersionRange()); assertEquals(currentVersion.toString(), upgrades[0].getUpgradedVersion()); } @@ -104,7 +160,7 @@ public void testCalculateUpgradePath410to412() { assertTrue(upgrades[3] instanceof Upgrade41120to41130); assertTrue(upgrades[4] instanceof Upgrade41120to41200); - assertTrue(Arrays.equals(new String[] {"4.11.0.0", "4.11.1.0"}, upgrades[1].getUpgradableVersionRange())); + assertArrayEquals(new String[]{"4.11.0.0", "4.11.1.0"}, upgrades[1].getUpgradableVersionRange()); assertEquals(currentVersion.toString(), upgrades[4].getUpgradedVersion()); } @@ -151,12 +207,12 @@ public void testFindUpgradePath452to490() { assertTrue(upgrades[5] instanceof Upgrade471to480); assertTrue(upgrades[6] instanceof Upgrade480to481); - assertTrue(Arrays.equals(new String[] {"4.8.1", currentVersion.toString()}, upgrades[upgrades.length - 1].getUpgradableVersionRange())); + assertArrayEquals(new String[]{"4.8.1", currentVersion.toString()}, upgrades[upgrades.length - 1].getUpgradableVersionRange()); assertEquals(currentVersion.toString(), upgrades[upgrades.length - 1].getUpgradedVersion()); } @Test - public void testCalculateUpgradePathUnkownDbVersion() { + public void testCalculateUpgradePathUnknownDbVersion() { final CloudStackVersion dbVersion = CloudStackVersion.parse("4.99.0.0"); assertNotNull(dbVersion); @@ -173,7 +229,7 @@ public void testCalculateUpgradePathUnkownDbVersion() { } @Test - public void testCalculateUpgradePathFromKownDbVersion() { + public void testCalculateUpgradePathFromKnownDbVersion() { final CloudStackVersion dbVersion = CloudStackVersion.parse("4.17.0.0"); assertNotNull(dbVersion); @@ -306,4 +362,64 @@ public void testCalculateUpgradePathFromSecurityReleaseToNextSecurityRelease() { assertEquals(upgrades.length + 1, upgradesFromSecurityReleaseToNext.length); assertTrue(upgradesFromSecurityReleaseToNext[upgradesFromSecurityReleaseToNext.length - 1] instanceof NoopDbUpgrade); } + + @Test + public void isStandalone() throws SQLException { + // simulate zero 'UP' hosts -> standalone + Mockito.when(resultSet.next()).thenReturn(true); + Mockito.when(resultSet.getInt(1)).thenReturn(0); + + final DatabaseUpgradeChecker checker = new DatabaseUpgradeChecker(); + assertTrue("DatabaseUpgradeChecker should be a standalone component", checker.isStandalone()); + } + + @Test + public void isNotStandalone() throws SQLException { + // simulate at least one 'UP' host -> not standalone + Mockito.when(resultSet.next()).thenReturn(true); + Mockito.when(resultSet.getInt(1)).thenReturn(1); + + final DatabaseUpgradeChecker checker = new DatabaseUpgradeChecker(); + assertFalse("DatabaseUpgradeChecker should not be a standalone component", checker.isStandalone()); + } + + @Test + public void testCalculateUpgradePath42010to42030() { + + final CloudStackVersion dbVersion = CloudStackVersion.parse("4.20.1.0"); + assertNotNull(dbVersion); + + final CloudStackVersion currentVersion = CloudStackVersion.parse("4.20.3.0"); + assertNotNull(currentVersion); + + final DatabaseUpgradeChecker checker = new DatabaseUpgradeChecker(); + final DbUpgrade[] upgrades = checker.calculateUpgradePath(dbVersion, currentVersion); + + assertNotNull(upgrades); + assertEquals(1, upgrades.length); + assertTrue(upgrades[0] instanceof Upgrade42020to42030); + + assertArrayEquals(new String[]{"4.20.2.0", "4.20.3.0"}, upgrades[0].getUpgradableVersionRange()); + assertEquals(currentVersion.toString(), upgrades[0].getUpgradedVersion()); + } + + @Test + public void testCalculateUpgradePath42010to42100() { + + final CloudStackVersion dbVersion = CloudStackVersion.parse("4.20.1.0"); + assertNotNull(dbVersion); + + final CloudStackVersion currentVersion = CloudStackVersion.parse("4.21.0.0"); + assertNotNull(currentVersion); + + final DatabaseUpgradeChecker checker = new DatabaseUpgradeChecker(); + final DbUpgrade[] upgrades = checker.calculateUpgradePath(dbVersion, currentVersion); + + assertNotNull(upgrades); + assertEquals(3, upgrades.length); + assertTrue(upgrades[0] instanceof Upgrade42020to42030); + assertTrue(upgrades[1] instanceof Upgrade42030to42040); + assertTrue(upgrades[2] instanceof Upgrade42040to42100); + assertEquals(currentVersion.toString(), upgrades[2].getUpgradedVersion()); + } } diff --git a/engine/schema/src/test/java/com/cloud/upgrade/SystemVmTemplateRegistrationTest.java b/engine/schema/src/test/java/com/cloud/upgrade/SystemVmTemplateRegistrationTest.java index dceb8e07b07d..51db952eb613 100644 --- a/engine/schema/src/test/java/com/cloud/upgrade/SystemVmTemplateRegistrationTest.java +++ b/engine/schema/src/test/java/com/cloud/upgrade/SystemVmTemplateRegistrationTest.java @@ -17,6 +17,7 @@ package com.cloud.upgrade; +import static com.cloud.upgrade.SystemVmTemplateRegistration.DEFAULT_SYSTEM_VM_GUEST_OS_NAME; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -25,24 +26,41 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +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.doThrow; +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.io.File; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Date; import java.util.List; +import java.util.Map; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.config.impl.ConfigurationVO; +import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; +import org.apache.cloudstack.storage.datastore.db.ImageStoreDetailsDao; +import org.apache.cloudstack.storage.datastore.db.ImageStoreVO; +import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; import org.apache.cloudstack.utils.security.DigestHelper; import org.apache.commons.lang3.StringUtils; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; @@ -53,15 +71,27 @@ import org.mockito.junit.MockitoJUnitRunner; import com.cloud.cpu.CPU; +import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.ClusterDao; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.DataCenterDetailsDao; import com.cloud.hypervisor.Hypervisor; +import com.cloud.storage.DataStoreRole; +import com.cloud.storage.GuestOSVO; +import com.cloud.storage.Storage; +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.VMTemplateDao; +import com.cloud.storage.dao.VMTemplateZoneDao; +import com.cloud.template.VirtualMachineTemplate; 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; +import com.cloud.vm.dao.VMInstanceDao; @RunWith(MockitoJUnitRunner.class) public class SystemVmTemplateRegistrationTest { @@ -72,10 +102,42 @@ public class SystemVmTemplateRegistrationTest { @Mock VMTemplateDao vmTemplateDao; + @Mock + GuestOSDao guestOSDao; + + @Mock + TemplateDataStoreDao templateDataStoreDao; + + @Mock + ConfigurationDao configurationDao; + + @Mock + DataCenterDao dataCenterDao; + + @Mock + DataCenterDetailsDao dataCenterDetailsDao; + + @Mock + VMTemplateZoneDao vmTemplateZoneDao; + + @Mock + ImageStoreDao imageStoreDao; + + @Mock + ImageStoreDetailsDao imageStoreDetailsDao; + + @Mock + VMInstanceDao vmInstanceDao; + @Spy @InjectMocks SystemVmTemplateRegistration systemVmTemplateRegistration = new SystemVmTemplateRegistration(); + @Before + public void setup() { + SystemVmTemplateRegistration.METADATA_TEMPLATE_LIST.clear(); + } + private void setupMetadataFile(MockedStatic mockedStatic, String content) { try { String location = "metadata.ini"; @@ -98,7 +160,7 @@ public void test_parseMetadataFile_noFile() { setupMetadataFile(mockedStatic, null); CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, SystemVmTemplateRegistration::parseMetadataFile); - assertTrue(exception.getMessage().contains("Failed to parse systemVM template metadata file")); + assertTrue(exception.getMessage().contains("Failed to parse system VM Template metadata file")); } } @@ -109,7 +171,7 @@ public void test_parseMetadataFile_invalidContent() { setupMetadataFile(mockedStatic, "abc"); CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, SystemVmTemplateRegistration::parseMetadataFile); - assertTrue(exception.getMessage().contains("Failed to parse systemVM template metadata file")); + assertTrue(exception.getMessage().contains("Failed to parse system VM Template metadata file")); } } @@ -141,21 +203,25 @@ public void test_parseMetadataFile_success() { String version = SystemVmTemplateRegistration.parseMetadataFile(); assertEquals("x.y.z.0", version); } - assertNull(SystemVmTemplateRegistration.NewTemplateMap.get("xenserver")); + assertNull(SystemVmTemplateRegistration.getMetadataTemplateDetails(Hypervisor.HypervisorType.XenServer, + CPU.CPUArch.getDefault())); SystemVmTemplateRegistration.MetadataTemplateDetails templateDetails = - SystemVmTemplateRegistration.NewTemplateMap.get("kvm-x86_64"); + SystemVmTemplateRegistration.getMetadataTemplateDetails(Hypervisor.HypervisorType.KVM, + CPU.CPUArch.amd64); assertNotNull(templateDetails); assertEquals(CPU.CPUArch.amd64, templateDetails.getArch()); assertEquals(Hypervisor.HypervisorType.KVM, templateDetails.getHypervisorType()); templateDetails = - SystemVmTemplateRegistration.NewTemplateMap.get("kvm-aarch64"); + SystemVmTemplateRegistration.getMetadataTemplateDetails(Hypervisor.HypervisorType.KVM, + CPU.CPUArch.arm64); assertNotNull(templateDetails); assertEquals(CPU.CPUArch.arm64, templateDetails.getArch()); assertEquals(Hypervisor.HypervisorType.KVM, templateDetails.getHypervisorType()); templateDetails = - SystemVmTemplateRegistration.NewTemplateMap.get("vmware"); + SystemVmTemplateRegistration.getMetadataTemplateDetails(Hypervisor.HypervisorType.VMware, + CPU.CPUArch.getDefault()); assertNotNull(templateDetails); - assertNull(templateDetails.getArch()); + assertEquals(CPU.CPUArch.getDefault(), templateDetails.getArch()); assertEquals(Hypervisor.HypervisorType.VMware, templateDetails.getHypervisorType()); } @@ -193,11 +259,10 @@ public void testValidateTemplateFile_fileNotFound() { SystemVmTemplateRegistration.MetadataTemplateDetails details = new SystemVmTemplateRegistration.MetadataTemplateDetails(Hypervisor.HypervisorType.KVM, "name", "file", "url", "checksum", CPU.CPUArch.amd64, "guestos"); - SystemVmTemplateRegistration.NewTemplateMap.put(SystemVmTemplateRegistration.getHypervisorArchKey( - details.getHypervisorType(), details.getArch()), details); + SystemVmTemplateRegistration.METADATA_TEMPLATE_LIST.add(details); doReturn(null).when(systemVmTemplateRegistration).getTemplateFile(details); try { - systemVmTemplateRegistration.validateTemplateFileForHypervisorAndArch(details.getHypervisorType(), + systemVmTemplateRegistration.getValidatedTemplateDetailsForHypervisorAndArch(details.getHypervisorType(), details.getArch()); fail("Expected CloudRuntimeException due to missing template file"); } catch (CloudRuntimeException e) { @@ -211,12 +276,11 @@ public void testValidateTemplateFile_checksumMismatch() { new SystemVmTemplateRegistration.MetadataTemplateDetails(Hypervisor.HypervisorType.KVM, "name", "file", "url", "checksum", CPU.CPUArch.amd64, "guestos"); File dummyFile = new File("dummy.txt"); - SystemVmTemplateRegistration.NewTemplateMap.put(SystemVmTemplateRegistration.getHypervisorArchKey( - details.getHypervisorType(), details.getArch()), details); + SystemVmTemplateRegistration.METADATA_TEMPLATE_LIST.add(details); doReturn(dummyFile).when(systemVmTemplateRegistration).getTemplateFile(details); - doReturn(true).when(systemVmTemplateRegistration).isTemplateFileChecksumDifferent(details, dummyFile); - try { - systemVmTemplateRegistration.validateTemplateFileForHypervisorAndArch(details.getHypervisorType(), + try (MockedStatic digestMock = Mockito.mockStatic(DigestHelper.class)) { + digestMock.when(() -> DigestHelper.calculateChecksum(dummyFile)).thenReturn("differentChecksum"); + systemVmTemplateRegistration.getValidatedTemplateDetailsForHypervisorAndArch(details.getHypervisorType(), details.getArch()); fail("Expected CloudRuntimeException due to checksum failure"); } catch (CloudRuntimeException e) { @@ -230,42 +294,55 @@ public void testValidateTemplateFile_success() { new SystemVmTemplateRegistration.MetadataTemplateDetails(Hypervisor.HypervisorType.KVM, "name", "file", "url", "checksum", CPU.CPUArch.amd64, "guestos"); File dummyFile = new File("dummy.txt"); - SystemVmTemplateRegistration.NewTemplateMap.put(SystemVmTemplateRegistration.getHypervisorArchKey( - details.getHypervisorType(), details.getArch()), details); + SystemVmTemplateRegistration.METADATA_TEMPLATE_LIST.add(details); doReturn(dummyFile).when(systemVmTemplateRegistration).getTemplateFile(details); - doReturn(false).when(systemVmTemplateRegistration).isTemplateFileChecksumDifferent(details, dummyFile); - systemVmTemplateRegistration.validateTemplateFileForHypervisorAndArch(details.getHypervisorType(), - details.getArch()); + try (MockedStatic digestMock = Mockito.mockStatic(DigestHelper.class)) { + digestMock.when(() -> DigestHelper.calculateChecksum(dummyFile)).thenReturn("checksum"); + systemVmTemplateRegistration.getValidatedTemplateDetailsForHypervisorAndArch(details.getHypervisorType(), + details.getArch()); + } } @Test - public void testValidateAndRegisterTemplate() { + public void testValidateAndAddExistingTemplateToStore() { + long zoneId = 1L; Hypervisor.HypervisorType hypervisor = Hypervisor.HypervisorType.KVM; - String name = "TestTemplate"; - Long storeId = 123L; VMTemplateVO templateVO = new VMTemplateVO(); - templateVO.setArch(CPU.CPUArch.x86); + templateVO.setHypervisorType(hypervisor); + templateVO.setArch(CPU.CPUArch.getDefault()); TemplateDataStoreVO templateDataStoreVO = new TemplateDataStoreVO(); + Long storeId = 123L; String filePath = "/dummy/path"; - doNothing().when(systemVmTemplateRegistration).validateTemplateFileForHypervisorAndArch(hypervisor, templateVO.getArch()); - doNothing().when(systemVmTemplateRegistration).registerTemplate(hypervisor, name, storeId, templateVO, templateDataStoreVO, filePath); - systemVmTemplateRegistration.validateAndRegisterTemplate(hypervisor, name, storeId, templateVO, templateDataStoreVO, filePath); - verify(systemVmTemplateRegistration).validateTemplateFileForHypervisorAndArch(eq(hypervisor), eq(templateVO.getArch())); - verify(systemVmTemplateRegistration).registerTemplate(eq(hypervisor), eq(name), eq(storeId), eq(templateVO), eq(templateDataStoreVO), eq(filePath)); + SystemVmTemplateRegistration.MetadataTemplateDetails details = + mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class); + doReturn(details).when(systemVmTemplateRegistration) + .getValidatedTemplateDetailsForHypervisorAndArch(hypervisor, templateVO.getArch()); + doNothing().when(systemVmTemplateRegistration).addExistingTemplateToStore(templateVO, details, + templateDataStoreVO, zoneId, storeId, filePath); + systemVmTemplateRegistration.validateAndAddTemplateToStore(templateVO, templateDataStoreVO, zoneId, storeId, + filePath); + verify(systemVmTemplateRegistration) + .getValidatedTemplateDetailsForHypervisorAndArch(hypervisor, templateVO.getArch()); + verify(systemVmTemplateRegistration).addExistingTemplateToStore(templateVO, details, templateDataStoreVO, + zoneId, storeId, filePath); } @Test - public void testValidateAndRegisterTemplateForNonExistingEntries() { + public void testValidateAndAddExistingTemplateToStoreForNonExistingEntries() { + long zoneId = 1L; Hypervisor.HypervisorType hypervisor = Hypervisor.HypervisorType.KVM; CPU.CPUArch arch = CPU.CPUArch.amd64; String name = "TestTemplateNonExisting"; - Pair storeUrlAndId = new Pair<>("nfs://dummy", 456L); + long storeId = 123L; String filePath = "/dummy/path/nonexisting"; - doNothing().when(systemVmTemplateRegistration).validateTemplateFileForHypervisorAndArch(hypervisor, arch); - doNothing().when(systemVmTemplateRegistration).registerTemplateForNonExistingEntries(hypervisor, arch, name, storeUrlAndId, filePath); - systemVmTemplateRegistration.validateAndRegisterTemplateForNonExistingEntries(hypervisor, arch, name, storeUrlAndId, filePath); - verify(systemVmTemplateRegistration).validateTemplateFileForHypervisorAndArch(eq(hypervisor), eq(arch)); - verify(systemVmTemplateRegistration).registerTemplateForNonExistingEntries(eq(hypervisor), eq(arch), eq(name), eq(storeUrlAndId), eq(filePath)); + SystemVmTemplateRegistration.MetadataTemplateDetails details = + mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class); + doReturn(details).when(systemVmTemplateRegistration) + .getValidatedTemplateDetailsForHypervisorAndArch(hypervisor, arch); + doNothing().when(systemVmTemplateRegistration).registerNewTemplate(name, details, zoneId, storeId, filePath); + systemVmTemplateRegistration.validateAndRegisterNewTemplate(hypervisor, arch, name, zoneId, storeId, filePath); + verify(systemVmTemplateRegistration).getValidatedTemplateDetailsForHypervisorAndArch(hypervisor, arch); + verify(systemVmTemplateRegistration).registerNewTemplate(name, details, zoneId, storeId, filePath); } @Test @@ -316,86 +393,73 @@ public void testGetTemplateFile_fileDoesNotExist_downloadSucceeds() { } @Test - public void testIsTemplateFileChecksumDifferent_noMismatch() { - SystemVmTemplateRegistration.MetadataTemplateDetails details = - Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class); - when(details.getChecksum()).thenReturn("dummyChecksum"); - File file = new File("dummy.txt"); - try (MockedStatic digestMock = Mockito.mockStatic(DigestHelper.class)) { - digestMock.when(() -> DigestHelper.calculateChecksum(file)).thenReturn("dummyChecksum"); - boolean result = systemVmTemplateRegistration.isTemplateFileChecksumDifferent(details, file); - assertFalse(result); - } - } - - @Test - public void testIsTemplateFileChecksumDifferent_mismatch() { - SystemVmTemplateRegistration.MetadataTemplateDetails details = - Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class); - when(details.getChecksum()).thenReturn("expectedChecksum"); - File file = new File("dummy.txt"); - try (MockedStatic digestMock = Mockito.mockStatic(DigestHelper.class)) { - digestMock.when(() -> DigestHelper.calculateChecksum(file)).thenReturn("actualChecksum"); - boolean result = systemVmTemplateRegistration.isTemplateFileChecksumDifferent(details, file); - assertTrue(result); - } - } - - @Test(expected = CloudRuntimeException.class) - public void testValidateTemplates_metadataTemplateFailure() { + public void testValidateTemplates_metadataTemplateSkip() { + Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.VMware; + CPU.CPUArch arch = CPU.CPUArch.arm64; List> list = new ArrayList<>(); - list.add(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64)); + list.add(new Pair<>(hypervisorType, arch)); systemVmTemplateRegistration.validateTemplates(list); + verify(systemVmTemplateRegistration, never()).getValidatedTemplateDetailsForHypervisorAndArch(hypervisorType, + arch); } @Test(expected = CloudRuntimeException.class) public void testValidateTemplates_fileFailure() { + Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; + CPU.CPUArch arch = CPU.CPUArch.amd64; List> list = new ArrayList<>(); - list.add(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64)); - + list.add(new Pair<>(hypervisorType, arch)); SystemVmTemplateRegistration.MetadataTemplateDetails details = Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class); - SystemVmTemplateRegistration.NewTemplateMap.put(SystemVmTemplateRegistration.getHypervisorArchKey( - Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64), details); + when(details.getHypervisorType()).thenReturn(hypervisorType); + when(details.getArch()).thenReturn(arch); File mockFile = Mockito.mock(File.class); + when(details.isFileChecksumDifferent(mockFile)).thenReturn(true); + SystemVmTemplateRegistration.METADATA_TEMPLATE_LIST.add(details); doReturn(mockFile).when(systemVmTemplateRegistration).getTemplateFile(details); - doReturn(true).when(systemVmTemplateRegistration).isTemplateFileChecksumDifferent(details, mockFile); systemVmTemplateRegistration.validateTemplates(list); } + @Test(expected = CloudRuntimeException.class) public void testValidateTemplates_downloadableFileNotFound() { CPU.CPUArch arch = SystemVmTemplateRegistration.DOWNLOADABLE_TEMPLATE_ARCH_TYPES.get(0); List> list = new ArrayList<>(); - list.add(new Pair<>(Hypervisor.HypervisorType.KVM, arch)); + Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; + list.add(new Pair<>(hypervisorType, arch)); SystemVmTemplateRegistration.MetadataTemplateDetails details = Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class); - SystemVmTemplateRegistration.NewTemplateMap.put(SystemVmTemplateRegistration.getHypervisorArchKey( - Hypervisor.HypervisorType.KVM, arch), details); + when(details.getHypervisorType()).thenReturn(hypervisorType); + when(details.getArch()).thenReturn(arch); + SystemVmTemplateRegistration.METADATA_TEMPLATE_LIST.add(details); doReturn(null).when(systemVmTemplateRegistration).getTemplateFile(details); systemVmTemplateRegistration.validateTemplates(list); } @Test public void testValidateTemplates_success() { + Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; + CPU.CPUArch arch = CPU.CPUArch.amd64; List> list = new ArrayList<>(); - list.add(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64)); - + list.add(new Pair<>(hypervisorType, arch)); SystemVmTemplateRegistration.MetadataTemplateDetails details = Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class); - SystemVmTemplateRegistration.NewTemplateMap.put(SystemVmTemplateRegistration.getHypervisorArchKey( - Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64), details); + when(details.getHypervisorType()).thenReturn(hypervisorType); + when(details.getArch()).thenReturn(arch); File mockFile = Mockito.mock(File.class); + when(details.isFileChecksumDifferent(mockFile)).thenReturn(false); + SystemVmTemplateRegistration.METADATA_TEMPLATE_LIST.add(details); doReturn(mockFile).when(systemVmTemplateRegistration).getTemplateFile(details); - doReturn(false).when(systemVmTemplateRegistration).isTemplateFileChecksumDifferent(details, mockFile); systemVmTemplateRegistration.validateTemplates(list); } @Test - public void testRegisterTemplatesForZone() { + public void testAddExistingTemplatesForZoneToStore() { long zoneId = 1L; String filePath = "dummyFilePath"; String nfsVersion = "nfs3"; Pair storeUrlAndId = new Pair<>("nfs://dummy", 100L); + String name = "existing"; + String url = "url"; doReturn(storeUrlAndId).when(systemVmTemplateRegistration).getNfsStoreInZone(zoneId); doReturn(nfsVersion).when(systemVmTemplateRegistration).getNfsVersion(storeUrlAndId.second()); try (MockedStatic mockedStatic = Mockito.mockStatic( @@ -407,21 +471,1171 @@ public void testRegisterTemplatesForZone() { doReturn(hypervisorArchList).when(clusterDao).listDistinctHypervisorsAndArchExcludingExternalType(zoneId); SystemVmTemplateRegistration.MetadataTemplateDetails details = Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class); - String name = "existing"; - Mockito.when(details.getArch()).thenReturn(CPU.CPUArch.getDefault()); - Mockito.when(details.getName()).thenReturn(name); + when(details.getArch()).thenReturn(CPU.CPUArch.getDefault()); + when(details.getName()).thenReturn(name); + when(details.getUrl()).thenReturn(url); mockedStatic.when(() -> SystemVmTemplateRegistration.getMetadataTemplateDetails(Mockito.any(), Mockito.any())).thenReturn(details); - when(systemVmTemplateRegistration.getRegisteredTemplate(name, arch)) - .thenReturn(null); - doNothing().when(systemVmTemplateRegistration).registerTemplateForNonExistingEntries( - hypervisorType, arch, - name, storeUrlAndId, filePath); + doNothing().when(systemVmTemplateRegistration).registerNewTemplate(name, details, zoneId, + storeUrlAndId.second(), filePath); systemVmTemplateRegistration.registerTemplatesForZone(zoneId, filePath); mockedStatic.verify(() -> SystemVmTemplateRegistration.mountStore(storeUrlAndId.first(), filePath, nfsVersion)); - verify(systemVmTemplateRegistration).registerTemplateForNonExistingEntries(hypervisorType, - arch, name, storeUrlAndId, filePath); + verify(systemVmTemplateRegistration).registerNewTemplate(name, details, zoneId, + storeUrlAndId.second(), filePath); + } + } + + @Test + public void updateOrRegisterSystemVmTemplate_UpdatesRegisteredTemplate() { + SystemVmTemplateRegistration.MetadataTemplateDetails templateDetails = Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class); + when(templateDetails.getName()).thenReturn("templateName"); + when(templateDetails.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + when(templateDetails.getArch()).thenReturn(CPU.CPUArch.amd64); + when(templateDetails.getUrl()).thenReturn("http://example.com/template"); + VMTemplateVO registeredTemplate = Mockito.mock(VMTemplateVO.class); + when(registeredTemplate.getId()).thenReturn(1L); + doReturn(registeredTemplate).when(systemVmTemplateRegistration).getRegisteredTemplate( + "templateName", Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64, "http://example.com/template"); + doNothing().when(systemVmTemplateRegistration).updateRegisteredTemplateDetails(1L, templateDetails, + null); + + boolean result = systemVmTemplateRegistration.updateOrRegisterSystemVmTemplate(templateDetails, + new ArrayList<>()); + + assertFalse(result); + verify(systemVmTemplateRegistration).updateRegisteredTemplateDetails(1L, templateDetails, null); + } + + @Test + public void updateOrRegisterSystemVmTemplate_SkipsUnusedHypervisorArch() { + SystemVmTemplateRegistration.MetadataTemplateDetails templateDetails = Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class); + when(templateDetails.getName()).thenReturn("templateName"); + when(templateDetails.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + when(templateDetails.getArch()).thenReturn(CPU.CPUArch.amd64); + when(templateDetails.getUrl()).thenReturn("http://example.com/template"); + doReturn(null).when(systemVmTemplateRegistration).getRegisteredTemplate( + "templateName", Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64, "http://example.com/template"); + doReturn(null).when(vmTemplateDao).findLatestTemplateByTypeAndHypervisorAndArch( + Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64, Storage.TemplateType.SYSTEM); + + boolean result = systemVmTemplateRegistration.updateOrRegisterSystemVmTemplate(templateDetails, new ArrayList<>()); + + assertFalse(result); + verify(systemVmTemplateRegistration, never()).registerTemplates(anyList()); + } + + @Test + public void updateOrRegisterSystemVmTemplate_RegistersNewTemplate() { + SystemVmTemplateRegistration.MetadataTemplateDetails templateDetails = Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class); + when(templateDetails.getName()).thenReturn("templateName"); + when(templateDetails.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + when(templateDetails.getArch()).thenReturn(CPU.CPUArch.amd64); + when(templateDetails.getUrl()).thenReturn("http://example.com/template"); + doReturn(null).when(systemVmTemplateRegistration).getRegisteredTemplate( + "templateName", Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64, "http://example.com/template"); + List> hypervisorsInUse = new ArrayList<>(); + hypervisorsInUse.add(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64)); + doNothing().when(systemVmTemplateRegistration).registerTemplates(hypervisorsInUse); + + boolean result = systemVmTemplateRegistration.updateOrRegisterSystemVmTemplate(templateDetails, hypervisorsInUse); + + assertTrue(result); + verify(systemVmTemplateRegistration).registerTemplates(eq(hypervisorsInUse)); + } + + @Test + public void updateOrRegisterSystemVmTemplate_ThrowsExceptionOnRegistrationFailure() { + SystemVmTemplateRegistration.MetadataTemplateDetails templateDetails = Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class); + when(templateDetails.getName()).thenReturn("templateName"); + when(templateDetails.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + when(templateDetails.getArch()).thenReturn(CPU.CPUArch.amd64); + when(templateDetails.getUrl()).thenReturn("http://example.com/template"); + doReturn(null).when(systemVmTemplateRegistration).getRegisteredTemplate( + "templateName", Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64, "http://example.com/template"); + List> hypervisorsInUse = new ArrayList<>(); + hypervisorsInUse.add(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64)); + doThrow(new CloudRuntimeException("Registration failed")).when(systemVmTemplateRegistration).registerTemplates(hypervisorsInUse); + + CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, + () -> systemVmTemplateRegistration.updateOrRegisterSystemVmTemplate(templateDetails, hypervisorsInUse)); + + assertTrue(exception.getMessage().contains("Failed to register")); + } + + @Test + public void updateRegisteredTemplateDetails_UpdatesTemplateSuccessfully() { + Long templateId = 1L; + Long zoneId = 2L; + SystemVmTemplateRegistration.MetadataTemplateDetails templateDetails = + Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class); + VMTemplateVO templateVO = Mockito.mock(VMTemplateVO.class); + String templateName = "templateName"; + when(templateVO.getName()).thenReturn(templateName); + GuestOSVO guestOS = Mockito.mock(GuestOSVO.class); + + when(templateDetails.getGuestOs()).thenReturn("Debian"); + when(templateDetails.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + when(vmTemplateDao.findById(templateId)).thenReturn(templateVO); + when(guestOSDao.findOneByDisplayName("Debian")).thenReturn(guestOS); + when(guestOS.getId()).thenReturn(10L); + when(vmTemplateDao.update(templateVO.getId(), templateVO)).thenReturn(true); + doNothing().when(systemVmTemplateRegistration).updateSystemVMEntries(templateId, Hypervisor.HypervisorType.KVM); + doNothing().when(systemVmTemplateRegistration).updateConfigurationParams(Hypervisor.HypervisorType.KVM, + templateName, zoneId); + + systemVmTemplateRegistration.updateRegisteredTemplateDetails(templateId, templateDetails, zoneId); + + verify(templateVO).setTemplateType(Storage.TemplateType.SYSTEM); + verify(templateVO).setGuestOSId(10); + verify(vmTemplateDao).update(templateVO.getId(), templateVO); + verify(systemVmTemplateRegistration).updateSystemVMEntries(templateId, Hypervisor.HypervisorType.KVM); + verify(systemVmTemplateRegistration).updateConfigurationParams(Hypervisor.HypervisorType.KVM, + templateName, zoneId); + } + + @Test + public void updateRegisteredTemplateDetails_ThrowsExceptionWhenUpdateFails() { + Long templateId = 1L; + Long zoneId = 2L; + SystemVmTemplateRegistration.MetadataTemplateDetails templateDetails = + Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class); + VMTemplateVO templateVO = Mockito.mock(VMTemplateVO.class); + + when(templateDetails.getGuestOs()).thenReturn("Debian"); + when(vmTemplateDao.findById(templateId)).thenReturn(templateVO); + when(vmTemplateDao.update(templateVO.getId(), templateVO)).thenReturn(false); + + CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, + () -> systemVmTemplateRegistration.updateRegisteredTemplateDetails(templateId, templateDetails, zoneId)); + + assertTrue(exception.getMessage().contains("Exception while updating template with id")); + verify(systemVmTemplateRegistration, never()).updateSystemVMEntries(anyLong(), any()); + verify(systemVmTemplateRegistration, never()).updateConfigurationParams(any(), any(), any()); + } + + @Test + public void updateRegisteredTemplateDetails_SkipsGuestOSUpdateWhenNotFound() { + Long templateId = 1L; + Long zoneId = 2L; + SystemVmTemplateRegistration.MetadataTemplateDetails templateDetails = + Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class); + VMTemplateVO templateVO = Mockito.mock(VMTemplateVO.class); + String templateName = "templateName"; + when(templateVO.getName()).thenReturn(templateName); + + when(templateDetails.getGuestOs()).thenReturn("NonExistentOS"); + when(templateDetails.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + when(vmTemplateDao.findById(templateId)).thenReturn(templateVO); + when(guestOSDao.findOneByDisplayName("NonExistentOS")).thenReturn(null); + when(vmTemplateDao.update(templateVO.getId(), templateVO)).thenReturn(true); + doNothing().when(systemVmTemplateRegistration).updateSystemVMEntries(templateId, Hypervisor.HypervisorType.KVM); + doNothing().when(systemVmTemplateRegistration).updateConfigurationParams(Hypervisor.HypervisorType.KVM, + templateName, zoneId); + + systemVmTemplateRegistration.updateRegisteredTemplateDetails(templateId, templateDetails, zoneId); + + verify(templateVO, never()).setGuestOSId(anyInt()); + verify(vmTemplateDao).update(templateVO.getId(), templateVO); + verify(systemVmTemplateRegistration).updateSystemVMEntries(templateId, Hypervisor.HypervisorType.KVM); + verify(systemVmTemplateRegistration).updateConfigurationParams(Hypervisor.HypervisorType.KVM, + templateName, zoneId); + } + + @Test + public void registerTemplatesForZone_SuccessfullyRegistersNewTemplate() { + long zoneId = 1L; + String storeMountPath = "/mnt/nfs"; + Pair storeUrlAndId = new Pair<>("nfs://dummy", 100L); + String nfsVersion = "nfs3"; + List> hypervisorArchList = new ArrayList<>(); + Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; + CPU.CPUArch arch = CPU.CPUArch.amd64; + hypervisorArchList.add(new Pair<>(hypervisorType, arch)); + SystemVmTemplateRegistration.MetadataTemplateDetails templateDetails = + Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class); + when(templateDetails.getHypervisorType()).thenReturn(hypervisorType); + when(templateDetails.getArch()).thenReturn(arch); + String name = "TestTemplate"; + String url = "http://example.com/template"; + when(templateDetails.getName()).thenReturn(name); + when(templateDetails.getUrl()).thenReturn(url); + doReturn(storeUrlAndId).when(systemVmTemplateRegistration).getNfsStoreInZone(zoneId); + doReturn(nfsVersion).when(systemVmTemplateRegistration).getNfsVersion(storeUrlAndId.second()); + doReturn(null).when(systemVmTemplateRegistration).getRegisteredTemplate( + name, hypervisorType, arch, url); + doNothing().when(systemVmTemplateRegistration).registerNewTemplate( + name, templateDetails, zoneId, storeUrlAndId.second(), storeMountPath); + doReturn(hypervisorArchList).when(clusterDao).listDistinctHypervisorsAndArchExcludingExternalType(zoneId); + try (MockedStatic mockedStatic = + Mockito.mockStatic(SystemVmTemplateRegistration.class)) { + mockedStatic.when(() -> SystemVmTemplateRegistration.getMetadataTemplateDetails( + hypervisorType, arch)).thenReturn(templateDetails); + + systemVmTemplateRegistration.registerTemplatesForZone(zoneId, storeMountPath); + + mockedStatic.verify(() -> SystemVmTemplateRegistration.mountStore( + eq(storeUrlAndId.first()), eq(storeMountPath), eq(nfsVersion)), times(1)); + verify(systemVmTemplateRegistration).registerNewTemplate( + templateDetails.getName(), templateDetails, zoneId, storeUrlAndId.second(), storeMountPath); + } + } + + @Test + public void registerTemplatesForZone_SkipsWhenTemplateDetailsNotFound() { + long zoneId = 1L; + String storeMountPath = "/mnt/nfs"; + Pair storeUrlAndId = new Pair<>("nfs://dummy", 100L); + String nfsVersion = "nfs3"; + List> hypervisorArchList = new ArrayList<>(); + Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; + CPU.CPUArch arch = CPU.CPUArch.amd64; + hypervisorArchList.add(new Pair<>(hypervisorType, arch)); + doReturn(storeUrlAndId).when(systemVmTemplateRegistration).getNfsStoreInZone(zoneId); + doReturn(nfsVersion).when(systemVmTemplateRegistration).getNfsVersion(storeUrlAndId.second()); + doReturn(hypervisorArchList).when(clusterDao).listDistinctHypervisorsAndArchExcludingExternalType(zoneId); + + try (MockedStatic mockedStatic = + Mockito.mockStatic(SystemVmTemplateRegistration.class)) { + mockedStatic.when(() -> SystemVmTemplateRegistration.getMetadataTemplateDetails( + hypervisorType, arch)).thenReturn(null); + + systemVmTemplateRegistration.registerTemplatesForZone(zoneId, storeMountPath); + + mockedStatic.verify(() -> SystemVmTemplateRegistration.mountStore( + eq(storeUrlAndId.first()), eq(storeMountPath), eq(nfsVersion)), times(1)); + verify(systemVmTemplateRegistration, never()).registerNewTemplate(any(), any(), anyLong(), anyLong(), any()); + } + } + + @Test + public void registerTemplatesForZone_AddsExistingTemplateToStore() { + long zoneId = 1L; + String storeMountPath = "/mnt/nfs"; + Pair storeUrlAndId = new Pair<>("nfs://dummy", 100L); + String nfsVersion = "nfs3"; + List> hypervisorArchList = new ArrayList<>(); + Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; + CPU.CPUArch arch = CPU.CPUArch.amd64; + hypervisorArchList.add(new Pair<>(hypervisorType, arch)); + SystemVmTemplateRegistration.MetadataTemplateDetails templateDetails = + Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class); + when(templateDetails.getHypervisorType()).thenReturn(hypervisorType); + when(templateDetails.getArch()).thenReturn(arch); + String name = "TestTemplate"; + String url = "http://example.com/template"; + when(templateDetails.getName()).thenReturn(name); + when(templateDetails.getUrl()).thenReturn(url); + VMTemplateVO templateVO = Mockito.mock(VMTemplateVO.class); + long templateId = 100L; + when(templateVO.getId()).thenReturn(templateId); + TemplateDataStoreVO templateDataStoreVO = Mockito.mock(TemplateDataStoreVO.class); + String installPath = "/template/install/path"; + when(templateDataStoreVO.getInstallPath()).thenReturn(installPath); + + doReturn(storeUrlAndId).when(systemVmTemplateRegistration).getNfsStoreInZone(zoneId); + doReturn(nfsVersion).when(systemVmTemplateRegistration).getNfsVersion(storeUrlAndId.second()); + doReturn(hypervisorArchList).when(clusterDao).listDistinctHypervisorsAndArchExcludingExternalType(zoneId); + doReturn(templateVO).when(systemVmTemplateRegistration).getRegisteredTemplate(name, hypervisorType, arch, url); + doReturn(templateDataStoreVO).when(templateDataStoreDao) + .findByStoreTemplate(storeUrlAndId.second(), templateId); + doReturn(false).when(systemVmTemplateRegistration).validateIfSeeded( + templateDataStoreVO, storeUrlAndId.first(), installPath, nfsVersion); + doNothing().when(systemVmTemplateRegistration).addExistingTemplateToStore( + templateVO, templateDetails, templateDataStoreVO, zoneId, storeUrlAndId.second(), storeMountPath); + doNothing().when(systemVmTemplateRegistration).updateRegisteredTemplateDetails( + templateId, templateDetails, zoneId); + + try (MockedStatic mockedStatic = + Mockito.mockStatic(SystemVmTemplateRegistration.class)) { + mockedStatic.when(() -> SystemVmTemplateRegistration.getMetadataTemplateDetails( + hypervisorType, arch)).thenReturn(templateDetails); + + systemVmTemplateRegistration.registerTemplatesForZone(zoneId, storeMountPath); + + verify(systemVmTemplateRegistration).addExistingTemplateToStore( + templateVO, templateDetails, templateDataStoreVO, zoneId, storeUrlAndId.second(), storeMountPath); + verify(systemVmTemplateRegistration).updateRegisteredTemplateDetails(templateId, templateDetails, zoneId); + } + } + + @Test + public void performTemplateRegistrationOperations_CreatesNewTemplateWhenNotExists() { + String name = "TestTemplate"; + String url = "http://example.com/template"; + String checksum = "abc123"; + Storage.ImageFormat format = Storage.ImageFormat.QCOW2; + long guestOsId = 1L; + Long storeId = 100L; + Long templateId = null; + String filePath = "/mnt/nfs"; + TemplateDataStoreVO templateDataStoreVO = null; + SystemVmTemplateRegistration.MetadataTemplateDetails templateDetails = Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class); + + when(templateDetails.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + when(templateDetails.getArch()).thenReturn(CPU.CPUArch.amd64); + doReturn(new VMTemplateVO()).when(vmTemplateDao).persist(any()); + doNothing().when(systemVmTemplateRegistration).createCrossZonesTemplateZoneRefEntries(anyLong()); + doNothing().when(systemVmTemplateRegistration).createTemplateStoreRefEntry(any()); + doNothing().when(systemVmTemplateRegistration).setupTemplateOnStore(anyString(), any(), anyString()); + doNothing().when(systemVmTemplateRegistration).readTemplateProperties(anyString(), any()); + doNothing().when(systemVmTemplateRegistration).updateTemplateDetails(any()); + + Long result = systemVmTemplateRegistration.performTemplateRegistrationOperations(name, templateDetails, url, checksum, format, guestOsId, storeId, templateId, filePath, templateDataStoreVO); + + assertNotNull(result); + verify(vmTemplateDao).persist(any()); + verify(systemVmTemplateRegistration).createCrossZonesTemplateZoneRefEntries(anyLong()); + verify(systemVmTemplateRegistration).createTemplateStoreRefEntry(any()); + verify(systemVmTemplateRegistration).setupTemplateOnStore(anyString(), any(), anyString()); + verify(systemVmTemplateRegistration).updateTemplateDetails(any()); + } + + @Test + public void performTemplateRegistrationOperations_UpdatesExistingTemplate() { + String name = "TestTemplate"; + String url = "http://example.com/template"; + String checksum = "abc123"; + Storage.ImageFormat format = Storage.ImageFormat.QCOW2; + long guestOsId = 1L; + Long storeId = 100L; + Long templateId = 1L; + String filePath = "/mnt/nfs"; + TemplateDataStoreVO templateDataStoreVO = Mockito.mock(TemplateDataStoreVO.class); + SystemVmTemplateRegistration.MetadataTemplateDetails templateDetails = Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class); + + when(templateDetails.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + when(templateDetails.getArch()).thenReturn(CPU.CPUArch.amd64); + doNothing().when(systemVmTemplateRegistration).createCrossZonesTemplateZoneRefEntries(anyLong()); + doNothing().when(systemVmTemplateRegistration).setupTemplateOnStore(anyString(), any(), anyString()); + doNothing().when(systemVmTemplateRegistration).readTemplateProperties(anyString(), any()); + doNothing().when(systemVmTemplateRegistration).updateTemplateDetails(any()); + + Long result = systemVmTemplateRegistration.performTemplateRegistrationOperations(name, templateDetails, url, checksum, format, guestOsId, storeId, templateId, filePath, templateDataStoreVO); + + assertNotNull(result); + assertEquals(templateId, result); + verify(vmTemplateDao, never()).persist(any()); + verify(systemVmTemplateRegistration).createCrossZonesTemplateZoneRefEntries(anyLong()); + verify(systemVmTemplateRegistration, never()).createTemplateStoreRefEntry(any()); + verify(systemVmTemplateRegistration).setupTemplateOnStore(anyString(), any(), anyString()); + verify(systemVmTemplateRegistration).updateTemplateDetails(any()); + } + + @Test + public void performTemplateRegistrationOperations_ThrowsExceptionWhenTemplateCreationFails() { + String name = "TestTemplate"; + String url = "http://example.com/template"; + String checksum = "abc123"; + Storage.ImageFormat format = Storage.ImageFormat.QCOW2; + long guestOsId = 1L; + Long storeId = 100L; + Long templateId = null; + String filePath = "/mnt/nfs"; + TemplateDataStoreVO templateDataStoreVO = null; + SystemVmTemplateRegistration.MetadataTemplateDetails templateDetails = Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class); + + when(templateDetails.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + when(templateDetails.getArch()).thenReturn(CPU.CPUArch.amd64); + doReturn(null).when(vmTemplateDao).persist(any()); + + assertThrows(CloudRuntimeException.class, () -> { + systemVmTemplateRegistration.performTemplateRegistrationOperations(name, templateDetails, url, checksum, format, guestOsId, storeId, templateId, filePath, templateDataStoreVO); + }); + + verify(vmTemplateDao).persist(any()); + verify(systemVmTemplateRegistration, never()).createCrossZonesTemplateZoneRefEntries(anyLong()); + verify(systemVmTemplateRegistration, never()).createTemplateStoreRefEntry(any()); + verify(systemVmTemplateRegistration, never()).setupTemplateOnStore(anyString(), any(), anyString()); + verify(systemVmTemplateRegistration, never()).updateTemplateDetails(any()); + } + + @Test + public void setupTemplateOnStore_ThrowsExceptionWhenScriptNotFound() { + String templateName = "templateName"; + String destTempFolder = "/tmp/folder"; + SystemVmTemplateRegistration.MetadataTemplateDetails templateDetails = + Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class); + + try (MockedStatic + + diff --git a/ui/src/components/offering/DiskOfferingForm.vue b/ui/src/components/offering/DiskOfferingForm.vue new file mode 100644 index 000000000000..f3f39647fefa --- /dev/null +++ b/ui/src/components/offering/DiskOfferingForm.vue @@ -0,0 +1,507 @@ +// 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/ActionButton.vue b/ui/src/components/view/ActionButton.vue index 7733efdf66e4..14128a5ce554 100644 --- a/ui/src/components/view/ActionButton.vue +++ b/ui/src/components/view/ActionButton.vue @@ -17,7 +17,7 @@ @@ -104,6 +106,9 @@ export default { case 'imagestore': this.scopeKey = 'imagestoreuuid' break + case 'managementserver': + this.scopeKey = 'managementserverid' + break default: this.scopeKey = '' } @@ -139,6 +144,13 @@ export default { handleSearch (value) { this.filter = value this.fetchData() + }, + handleConfigRefresh (name, updatedRecord) { + if (!name || !updatedRecord) return + const index = this.items.findIndex(item => item.name === name) + if (index !== -1) { + this.items.splice(index, 1, updatedRecord) + } } } } diff --git a/ui/src/components/view/StatsTab.vue b/ui/src/components/view/StatsTab.vue index 8703b6b995de..d8739ece6c14 100644 --- a/ui/src/components/view/StatsTab.vue +++ b/ui/src/components/view/StatsTab.vue @@ -41,9 +41,9 @@ + @change="updateVirtualMachineStats"> - {{ $t('1 hour') }} + {{ $t('label.duration.1hour') }} {{ $t('label.duration.6hours') }} @@ -62,6 +62,16 @@ + {{$t('label.auto.refresh.statistics')}} + + {{$t('label.auto.refresh.statistics.none')}} + 5s + 30s + 1min + 5min +
@@ -297,6 +307,8 @@ export default { selectedDiskUnitOfMeasurement: 'KiB', diskUnitsOfMeasurement: ['KiB', 'MiB', 'GiB'], chartLabels: [], + refreshTime: '0', + refreshIntervalId: null, resourceUsageHistory: { cpu: [], memory: { @@ -334,6 +346,9 @@ export default { mounted () { this.fetchData() }, + unmounted () { + window.clearInterval(this.refreshIntervalId) + }, computed: { statsRetentionTime () { if (this.resourceType === 'Volume') { @@ -371,6 +386,15 @@ export default { return } this.fetchData() + }, + refreshTime: function () { + this.updateVirtualMachineStats() + if (this.refreshTime === '0') return window.clearInterval(this.refreshIntervalId) + + window.clearInterval(this.refreshIntervalId) + this.refreshIntervalId = window.setInterval(() => { + this.updateVirtualMachineStats() + }, parseInt(this.refreshTime)) } }, methods: { @@ -398,26 +422,10 @@ export default { this.resourceTypeToShowInfo = resource this.showResourceInfoModal = true }, - handleDurationChange () { - var now = this.getEndDate() - var start = new Date(now) - switch (this.durationSelectorValue) { - case '6hours': - start.setHours(start.getHours() - 6) - break - case '12hours': - start.setHours(start.getHours() - 12) - break - case 'day': - start.setDate(start.getDate() - 1) - break - case 'week': - start.setDate(start.getDate() - 7) - break - default: - start.setHours(start.getHours() - 1) - } - this.handleSubmit({ startDate: start, endDate: now }) + updateVirtualMachineStats () { + const start = this.getStartDate() + const end = this.getEndDate() + this.handleSubmit({ startDate: start, endDate: end }) }, handleSubmit (values) { if (values.startDate) { @@ -437,9 +445,19 @@ export default { this.showFilterStatsModal = false }, getStartDate () { - var now = new Date() - now.setHours(now.getHours() - 1) - return now + const now = new Date() + switch (this.durationSelectorValue) { + case '6hours': + return now.setHours(now.getHours() - 6) + case '12hours': + return now.setHours(now.getHours() - 12) + case 'day': + return now.setDate(now.getDate() - 1) + case 'week': + return now.setDate(now.getDate() - 7) + default: + return now.setHours(now.getHours() - 1) + } }, getEndDate () { return new Date() diff --git a/ui/src/components/view/TestWebhookDeliveryView.vue b/ui/src/components/view/TestWebhookDeliveryView.vue index c5b29a17e795..364a7dce3b47 100644 --- a/ui/src/components/view/TestWebhookDeliveryView.vue +++ b/ui/src/components/view/TestWebhookDeliveryView.vue @@ -126,7 +126,7 @@ export default { collapseKey: undefined, loading: false, testDeliveryInterval: null, - testDeliveryIntervalCouter: 0 + testDeliveryIntervalCounter: 0 } }, beforeCreate () { @@ -154,8 +154,8 @@ export default { return (duration > 0 ? duration / 1000.0 : 0) + '' }, computedOverlayStyle () { - var opacity = this.testDeliveryIntervalCouter <= 10.0 ? 0 : 0.3 - var width = this.testDeliveryIntervalCouter + var opacity = this.testDeliveryIntervalCounter <= 10.0 ? 0 : 0.3 + var width = this.testDeliveryIntervalCounter return 'opacity: ' + opacity + '; width: ' + width + '%;' } }, @@ -168,7 +168,7 @@ export default { if (this.testDeliveryInterval) { clearInterval(this.testDeliveryInterval) } - this.testDeliveryIntervalCouter = 0 + this.testDeliveryIntervalCounter = 0 }, testWebhookDelivery () { this.resetTestDeliveryInterval() @@ -186,7 +186,7 @@ export default { params.payloadUrl = this.payloadUrl } if (this.sslVerification) { - params.payload = this.sslVerification + params.sslVerification = this.sslVerification } if (this.secretKey) { params.secretKey = this.secretKey @@ -223,8 +223,8 @@ export default { this.resetTestDeliveryInterval() return } - this.testDeliveryIntervalCouter = this.testDeliveryIntervalCouter + 1 - if (this.testDeliveryIntervalCouter >= 100) { + this.testDeliveryIntervalCounter = this.testDeliveryIntervalCounter + 1 + if (this.testDeliveryIntervalCounter >= 100) { this.executeTestWebhookDeliveryOrReset() } }, this.timedDeliveryWait / 100) diff --git a/ui/src/components/view/VgpuProfilesTab.vue b/ui/src/components/view/VgpuProfilesTab.vue index b5c2af591c2d..4480c4ade0a3 100644 --- a/ui/src/components/view/VgpuProfilesTab.vue +++ b/ui/src/components/view/VgpuProfilesTab.vue @@ -1,17 +1,17 @@ // Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file +// or more contributor license agreements. See the NOTICE file // distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file +// 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 +// with the License. You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// 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 +// 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/VolumeStoragePoolSelectForm.vue b/ui/src/components/view/VolumeStoragePoolSelectForm.vue index eea416faa1a4..9981418ee14d 100644 --- a/ui/src/components/view/VolumeStoragePoolSelectForm.vue +++ b/ui/src/components/view/VolumeStoragePoolSelectForm.vue @@ -25,6 +25,15 @@ :autoAssignAllowed="autoAssignAllowed" @select="handleSelect" /> + + + + +
@@ -36,11 +45,13 @@ + + diff --git a/ui/src/components/view/buttons/ExportToCsvButton.vue b/ui/src/components/view/buttons/ExportToCsvButton.vue new file mode 100644 index 000000000000..cc084bdf1397 --- /dev/null +++ b/ui/src/components/view/buttons/ExportToCsvButton.vue @@ -0,0 +1,43 @@ +// 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/charts/BarChart.vue b/ui/src/components/view/charts/BarChart.vue new file mode 100644 index 000000000000..a27069163d35 --- /dev/null +++ b/ui/src/components/view/charts/BarChart.vue @@ -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. + + + + diff --git a/ui/src/components/view/stats/ResourceStatsInfo.vue b/ui/src/components/view/stats/ResourceStatsInfo.vue index 6898141bdfc5..104283436626 100644 --- a/ui/src/components/view/stats/ResourceStatsInfo.vue +++ b/ui/src/components/view/stats/ResourceStatsInfo.vue @@ -1,17 +1,17 @@ // Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file +// or more contributor license agreements. See the NOTICE file // distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file +// 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 +// with the License. You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// 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 +// 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/stats/ResourceStatsLineChart.vue b/ui/src/components/view/stats/ResourceStatsLineChart.vue index fa15ea398a5e..f1afbefd155d 100644 --- a/ui/src/components/view/stats/ResourceStatsLineChart.vue +++ b/ui/src/components/view/stats/ResourceStatsLineChart.vue @@ -1,17 +1,17 @@ // Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file +// or more contributor license agreements. See the NOTICE file // distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file +// 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 +// with the License. You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// 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 +// KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. @@ -217,7 +217,7 @@ export default { data: element.data.map(d => d.stat), hidden: this.hideLine(element.data.map(d => d.stat)), pointRadius: element.pointRadius, - fill: 'origin' + fill: element.fill || 'origin' } ) } diff --git a/ui/src/components/widgets/Breadcrumb.vue b/ui/src/components/widgets/Breadcrumb.vue index 147e779502bf..4723417f5398 100644 --- a/ui/src/components/widgets/Breadcrumb.vue +++ b/ui/src/components/widgets/Breadcrumb.vue @@ -100,7 +100,7 @@ export default { this.breadList = [] this.$route.matched.forEach((item, idx) => { const parent = this.$route.matched[idx - 1] - if (item && parent && parent.name !== 'index' && !item.path.endsWith(':id')) { + if (item && parent && parent.name !== 'index' && !item.path.endsWith(':id') && !item.path.endsWith(':id(.*)')) { this.breadList.pop() } this.breadList.push(item) diff --git a/ui/src/components/widgets/Console.vue b/ui/src/components/widgets/Console.vue index 311057437958..edee7498991a 100644 --- a/ui/src/components/widgets/Console.vue +++ b/ui/src/components/widgets/Console.vue @@ -17,9 +17,17 @@ - + - + - {{ option[optionLabelKey] }} + {{ optionLabelFn ? optionLabelFn(option) : option[optionLabelKey] }} @@ -120,10 +124,18 @@ export default { type: String, default: 'name' }, + optionLabelFn: { + type: Function, + default: null + }, defaultOption: { type: Object, default: null }, + allowClear: { + type: Boolean, + default: false + }, showIcon: { type: Boolean, default: true @@ -135,6 +147,14 @@ export default { pageSize: { type: Number, default: null + }, + autoSelectFirstOption: { + type: Boolean, + default: false + }, + selectFirstOption: { + type: Boolean, + default: false } }, data () { @@ -147,11 +167,13 @@ export default { searchTimer: null, scrollHandlerAttached: false, preselectedOptionValue: null, - successiveFetches: 0 + successiveFetches: 0, + canSelectFirstOption: false, + hasAutoSelectedFirst: false } }, created () { - this.addDefaultOptionIfNeeded(true) + this.addDefaultOptionIfNeeded() }, mounted () { this.preselectedOptionValue = this.$attrs.value @@ -166,6 +188,36 @@ export default { }, formattedSearchFooterMessage () { return `${this.$t('label.showing.results.for').replace('%x', this.searchQuery)}` + }, + selectableOptions () { + const currentValue = this.$attrs.value + // Only filter out null/empty options when the current value is also null/undefined/empty + // This prevents such options from being selected and allows the placeholder to show instead + if (currentValue === null || currentValue === undefined || currentValue === '') { + return this.options.filter(option => { + const optionValue = option[this.optionValueKey] + return optionValue !== null && optionValue !== undefined && optionValue !== '' + }) + } + // When a valid value is selected, show all options + return this.options + }, + apiOptionsCount () { + if (this.defaultOption) { + const defaultOptionValue = this.defaultOption[this.optionValueKey] + return this.options.filter(option => option[this.optionValueKey] !== defaultOptionValue).length + } + return this.options.length + }, + preselectedMatchValue () { + // Extract the first value from preselectedOptionValue if it's an array, otherwise return the value itself + if (!this.preselectedOptionValue) return null + return Array.isArray(this.preselectedOptionValue) ? this.preselectedOptionValue[0] : this.preselectedOptionValue + }, + preselectedMatch () { + // Find the matching option for the preselected value + if (!this.preselectedMatchValue) return null + return this.options.find(entry => entry[this.optionValueKey] === this.preselectedMatchValue) || null } }, watch: { @@ -208,8 +260,10 @@ export default { }).catch(error => { this.$notifyError(error) }).finally(() => { + this.canSelectFirstOption = true if (this.successiveFetches === 0) { this.loading = false + this.autoSelectFirstOptionIfNeeded() } }) }, @@ -218,13 +272,18 @@ export default { (Array.isArray(this.preselectedOptionValue) && this.preselectedOptionValue.length === 0) || this.successiveFetches >= this.maxSuccessiveFetches) { this.resetPreselectedOptionValue() + if (!this.canSelectFirstOption && this.autoSelectFirstOption && this.options.length > 0) { + this.$nextTick(() => { + this.preselectedOptionValue = this.options[0][this.optionValueKey] + this.onChange(this.preselectedOptionValue) + }) + } return } - const matchValue = Array.isArray(this.preselectedOptionValue) ? this.preselectedOptionValue[0] : this.preselectedOptionValue - const match = this.options.find(entry => entry[this.optionValueKey] === matchValue) - if (!match) { + if (!this.preselectedMatch) { this.successiveFetches++ - if (this.options.length < this.totalCount) { + // Exclude defaultOption from count when comparing with totalCount + if (this.apiOptionsCount < this.totalCount) { this.fetchItems() } else { this.resetPreselectedOptionValue() @@ -232,13 +291,14 @@ export default { return } if (Array.isArray(this.preselectedOptionValue) && this.preselectedOptionValue.length > 1) { - this.preselectedOptionValue = this.preselectedOptionValue.filter(o => o !== match) + this.preselectedOptionValue = this.preselectedOptionValue.filter(o => o !== this.preselectedMatchValue) } else { this.resetPreselectedOptionValue() } }, addDefaultOptionIfNeeded () { if (this.defaultOption) { + this.canSelectFirstOption = true this.options.push(this.defaultOption) } }, @@ -246,6 +306,36 @@ export default { this.preselectedOptionValue = null this.successiveFetches = 0 }, + autoSelectFirstOptionIfNeeded () { + if (!this.selectFirstOption || this.hasAutoSelectedFirst) { + return + } + // Don't auto-select if there's a preselected value being fetched + if (this.preselectedOptionValue) { + return + } + const currentValue = this.$attrs.value + if (currentValue !== undefined && currentValue !== null && currentValue !== '') { + return + } + if (this.options.length === 0) { + return + } + if (this.searchQuery && this.searchQuery.length > 0) { + return + } + // Only auto-select after initial load is complete (no more successive fetches) + if (this.successiveFetches > 0) { + return + } + const firstOption = this.options[0] + if (firstOption) { + const firstValue = firstOption[this.optionValueKey] + this.hasAutoSelectedFirst = true + this.$emit('change-option-value', firstValue) + this.$emit('change-option', firstOption) + } + }, onSearchTimed (value) { clearTimeout(this.searchTimer) this.searchTimer = setTimeout(() => { @@ -264,7 +354,8 @@ export default { }, onScroll (e) { const nearBottom = e.target.scrollTop + e.target.clientHeight >= e.target.scrollHeight - 10 - const hasMore = this.options.length < this.totalCount + // Exclude defaultOption from count when comparing with totalCount + const hasMore = this.apiOptionsCount < this.totalCount if (nearBottom && hasMore && !this.loading) { this.fetchItems() } diff --git a/ui/src/components/widgets/OsLogo.vue b/ui/src/components/widgets/OsLogo.vue index 643953012c18..f19aac56a1a6 100644 --- a/ui/src/components/widgets/OsLogo.vue +++ b/ui/src/components/widgets/OsLogo.vue @@ -31,6 +31,9 @@ - - diff --git a/ui/src/components/widgets/Status.vue b/ui/src/components/widgets/Status.vue index 6b9f71b5de84..3b078b5fe3e3 100644 --- a/ui/src/components/widgets/Status.vue +++ b/ui/src/components/widgets/Status.vue @@ -106,6 +106,24 @@ export default { case 'no': state = this.$t('label.no') break + case 'backedup': + state = this.$t('label.backedup') + break + case 'backingup': + state = this.$t('label.backingup') + break + case 'allocated': + state = this.$t('label.allocated') + break + case 'queued': + state = this.$t('label.queued') + break + case 'restoring': + state = this.$t('label.restoring') + break + case 'failed': + state = this.$t('label.failed') + break } return state.charAt(0).toUpperCase() + state.slice(1) } @@ -135,6 +153,8 @@ export default { case 'primary': case 'managed': case 'yes': + case 'compressed': + case 'valid': status = 'success' break case 'alert': @@ -151,6 +171,9 @@ export default { case 'failed': case 'unmanaged': case 'no': + case 'notvalid': + case 'unabletovalidate': + case 'compressionerror': status = 'error' break case 'migrating': @@ -159,6 +182,8 @@ export default { case 'stopping': case 'upgrading': case 'inprogress': + case 'validating': + case 'compressing': status = 'processing' break case 'allocated': @@ -175,6 +200,7 @@ export default { case 'warning': case 'backup': case 'partiallyallocated': + case 'uncompressed': status = 'warning' break } diff --git a/ui/src/composables/useServiceCapabilityParams.js b/ui/src/composables/useServiceCapabilityParams.js new file mode 100644 index 000000000000..734f30ec3f4a --- /dev/null +++ b/ui/src/composables/useServiceCapabilityParams.js @@ -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. + +export function buildServiceCapabilityParams (params, values, selectedServiceProviderMap, registeredServicePackages) { + const supportedServices = Object.keys(selectedServiceProviderMap) + params.supportedservices = supportedServices.join(',') + for (const k in supportedServices) { + params[`serviceProviderList[${k}].service`] = supportedServices[k] + params[`serviceProviderList[${k}].provider`] = selectedServiceProviderMap[supportedServices[k]] + } + let serviceCapabilityIndex = 0 + if (supportedServices.includes('Connectivity')) { + if (values.supportsstrechedl2subnet === true) { + params[`serviceCapabilityList[${serviceCapabilityIndex}].service`] = 'Connectivity' + params[`serviceCapabilityList[${serviceCapabilityIndex}].capabilitytype`] = 'RegionLevelVpc' + params[`serviceCapabilityList[${serviceCapabilityIndex}].capabilityvalue`] = true + serviceCapabilityIndex++ + } + if (values.supportspublicaccess === true) { + params[`serviceCapabilityList[${serviceCapabilityIndex}].service`] = 'Connectivity' + params[`serviceCapabilityList[${serviceCapabilityIndex}].capabilitytype`] = 'DistributedRouter' + params[`serviceCapabilityList[${serviceCapabilityIndex}].capabilityvalue`] = true + serviceCapabilityIndex++ + } + delete params.supportsstrechedl2subnet + delete params.supportspublicaccess + } + // SourceNat capabilities + if (supportedServices.includes('SourceNat')) { + if (values.redundantroutercapability === true) { + params[`serviceCapabilityList[${serviceCapabilityIndex}].service`] = 'SourceNat' + params[`serviceCapabilityList[${serviceCapabilityIndex}].capabilitytype`] = 'RedundantRouter' + params[`serviceCapabilityList[${serviceCapabilityIndex}].capabilityvalue`] = true + serviceCapabilityIndex++ + } + params[`servicecapabilitylist[${serviceCapabilityIndex}].service`] = 'SourceNat' + params[`servicecapabilitylist[${serviceCapabilityIndex}].capabilitytype`] = 'SupportedSourceNatTypes' + params[`servicecapabilitylist[${serviceCapabilityIndex}].capabilityvalue`] = values.sourcenattype + serviceCapabilityIndex++ + delete params.redundantroutercapability + delete params.sourcenattype + } else if (values.redundantroutercapability === true) { + params[`serviceCapabilityList[${serviceCapabilityIndex}].service`] = 'Gateway' + params[`serviceCapabilityList[${serviceCapabilityIndex}].capabilitytype`] = 'RedundantRouter' + params[`serviceCapabilityList[${serviceCapabilityIndex}].capabilityvalue`] = true + serviceCapabilityIndex++ + } + // StaticNat capabilities + if (supportedServices.includes('SourceNat')) { + if (values.elasticip === true) { + params[`servicecapabilitylist[${serviceCapabilityIndex}].service`] = 'StaticNat' + params[`servicecapabilitylist[${serviceCapabilityIndex}].capabilitytype`] = 'ElasticIp' + params[`servicecapabilitylist[${serviceCapabilityIndex}].capabilityvalue`] = true + serviceCapabilityIndex++ + } + if (values.elasticip === true || values.associatepublicip === true) { + params[`servicecapabilitylist[${serviceCapabilityIndex}].service`] = 'StaticNat' + params[`servicecapabilitylist[${serviceCapabilityIndex}].capabilitytype`] = 'associatePublicIP' + params[`servicecapabilitylist[${serviceCapabilityIndex}].capabilityvalue`] = values.associatepublicip + serviceCapabilityIndex++ + } + delete params.elasticip + delete params.associatepublicip + } + // Lb capabilities + if (supportedServices.includes('Lb')) { + if ('vmautoscalingcapability' in values) { + params[`servicecapabilitylist[${serviceCapabilityIndex}].service`] = 'lb' + params[`servicecapabilitylist[${serviceCapabilityIndex}].capabilitytype`] = 'VmAutoScaling' + params[`servicecapabilitylist[${serviceCapabilityIndex}].capabilityvalue`] = values.vmautoscalingcapability + serviceCapabilityIndex++ + } + if (values.elasticlb === true) { + params[`servicecapabilitylist[${serviceCapabilityIndex}].service`] = 'lb' + params[`servicecapabilitylist[${serviceCapabilityIndex}].capabilitytype`] = 'ElasticLb' + params[`servicecapabilitylist[${serviceCapabilityIndex}].capabilityvalue`] = true + serviceCapabilityIndex++ + } + if (values.inlinemode === true && ((selectedServiceProviderMap.Lb === 'F5BigIp') || (selectedServiceProviderMap.Lb === 'Netscaler'))) { + params[`servicecapabilitylist[${serviceCapabilityIndex}].service`] = 'lb' + params[`servicecapabilitylist[${serviceCapabilityIndex}].capabilitytype`] = 'InlineMode' + params[`servicecapabilitylist[${serviceCapabilityIndex}].capabilityvalue`] = values.inlinemode + serviceCapabilityIndex++ + } + params[`servicecapabilitylist[${serviceCapabilityIndex}].service`] = 'lb' + params[`servicecapabilitylist[${serviceCapabilityIndex}].capabilitytype`] = 'SupportedLbIsolation' + params[`servicecapabilitylist[${serviceCapabilityIndex}].capabilityvalue`] = values.isolation || 'dedicated' + serviceCapabilityIndex++ + if (selectedServiceProviderMap.Lb === 'InternalLbVm') { + params[`servicecapabilitylist[${serviceCapabilityIndex}].service`] = 'lb' + params[`servicecapabilitylist[${serviceCapabilityIndex}].capabilitytype`] = 'lbSchemes' + params[`servicecapabilitylist[${serviceCapabilityIndex}].capabilityvalue`] = 'internal' + serviceCapabilityIndex++ + } + if ('netscalerservicepackages' in values && + registeredServicePackages.length > values.netscalerservicepackages && + 'netscalerservicepackagesdescription' in values) { + params['details[0].servicepackageuuid'] = registeredServicePackages[values.netscalerservicepackages].id + params['details[1].servicepackagedescription'] = values.netscalerservicepackagesdescription + } + } +} + +/** + * Build the VPC service capability params for Add/Clone VPC Offering forms. + * Handles: RegionLevelVpc, DistributedRouter, RedundantRouter (SourceNat/Gateway) + */ +export function buildVpcServiceCapabilityParams (params, values, selectedServiceProviderMap, isVpcVirtualRouterForAtLeastOneService) { + const supportedServices = Object.keys(selectedServiceProviderMap) + let serviceCapabilityIndex = 0 + if (supportedServices.includes('Connectivity')) { + if (values.regionlevelvpc === true) { + params[`serviceCapabilityList[${serviceCapabilityIndex}].service`] = 'Connectivity' + params[`serviceCapabilityList[${serviceCapabilityIndex}].capabilitytype`] = 'RegionLevelVpc' + params[`serviceCapabilityList[${serviceCapabilityIndex}].capabilityvalue`] = true + serviceCapabilityIndex++ + } + if (values.distributedrouter === true) { + params[`serviceCapabilityList[${serviceCapabilityIndex}].service`] = 'Connectivity' + params[`serviceCapabilityList[${serviceCapabilityIndex}].capabilitytype`] = 'DistributedRouter' + params[`serviceCapabilityList[${serviceCapabilityIndex}].capabilityvalue`] = true + serviceCapabilityIndex++ + } + } + if (supportedServices.includes('SourceNat') && values.redundantrouter === true) { + params[`serviceCapabilityList[${serviceCapabilityIndex}].service`] = 'SourceNat' + params[`serviceCapabilityList[${serviceCapabilityIndex}].capabilitytype`] = 'RedundantRouter' + params[`serviceCapabilityList[${serviceCapabilityIndex}].capabilityvalue`] = true + serviceCapabilityIndex++ + } else if (values.redundantrouter === true) { + params[`serviceCapabilityList[${serviceCapabilityIndex}].service`] = 'Gateway' + params[`serviceCapabilityList[${serviceCapabilityIndex}].capabilitytype`] = 'RedundantRouter' + params[`serviceCapabilityList[${serviceCapabilityIndex}].capabilityvalue`] = true + serviceCapabilityIndex++ + } + if (values.serviceofferingid && isVpcVirtualRouterForAtLeastOneService) { + params.serviceofferingid = values.serviceofferingid + } +} diff --git a/ui/src/config/router.js b/ui/src/config/router.js index 582fbaaf2f35..b9c60bcd0c21 100644 --- a/ui/src/config/router.js +++ b/ui/src/config/router.js @@ -28,9 +28,11 @@ import compute from '@/config/section/compute' import storage from '@/config/section/storage' import network from '@/config/section/network' import image from '@/config/section/image' +import kms from '@/config/section/kms' import project from '@/config/section/project' import event from '@/config/section/event' import user from '@/config/section/user' +import keyPair from '@/config/section/keypair' import account from '@/config/section/account' import domain from '@/config/section/domain' import role from '@/config/section/role' @@ -81,6 +83,7 @@ function generateRouterMap (section) { filters: child.filters, params: child.params ? child.params : {}, columns: child.columns, + advisories: !vueProps.$config.advisoriesDisabled ? child.advisories : undefined, details: child.details, searchFilters: child.searchFilters, related: child.related, @@ -92,7 +95,7 @@ function generateRouterMap (section) { hideChildrenInMenu: true, children: [ { - path: '/' + child.name + '/:id', + path: '/' + child.name + '/:id(.*)', hidden: child.hidden, meta: { title: child.title, @@ -147,7 +150,7 @@ function generateRouterMap (section) { map.meta.tabs = section.tabs map.children = [{ - path: '/' + section.name + '/:id', + path: '/' + section.name + '/:id(.*)', actions: section.actions ? section.actions : [], meta: { title: section.title, @@ -180,6 +183,10 @@ function generateRouterMap (section) { map.meta.columns = section.columns } + if (!vueProps.$config.advisoriesDisabled && section.advisories) { + map.meta.advisories = section.advisories + } + if (section.actions) { map.meta.actions = section.actions } @@ -213,9 +220,11 @@ export function asyncRouterMap () { generateRouterMap(storage), generateRouterMap(network), generateRouterMap(image), + generateRouterMap(kms), generateRouterMap(event), generateRouterMap(project), generateRouterMap(user), + generateRouterMap(keyPair), generateRouterMap(role), generateRouterMap(account), generateRouterMap(domain), @@ -313,6 +322,11 @@ export const constantRouterMap = [ path: 'resetPassword', name: 'resetPassword', component: () => import(/* webpackChunkName: "auth" */ '@/views/auth/ResetPassword') + }, + { + path: 'forceChangePassword', + name: 'forceChangePassword', + component: () => import(/* webpackChunkName: "auth" */ '@/views/iam/ForceChangePassword') } ] }, diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js index 7688566df423..b6e83b5eb438 100644 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@ -18,6 +18,18 @@ import { shallowRef, defineAsyncComponent } from 'vue' import store from '@/store' import { isZoneCreated } from '@/utils/zone' +import { getAPI, postAPI, getBaseUrl } from '@/api' +import { getLatestKubernetesIsoParams } from '@/utils/acsrepo' +import kubernetesIcon from '@/assets/icons/kubernetes.svg?inline' + +const attachedIsoCount = (record) => (record.isos && record.isos.length) || (record.isoid ? 1 : 0) +// Server pre-computes the effective cap (cluster-scoped vm.iso.max.count clamped to the +// hypervisor's own limit). Fall back to the hypervisor floor for older servers. +const isoMaxCount = (record) => record.isomaxcount != null + ? record.isomaxcount + : (record.hypervisor === 'KVM' ? 2 : 1) +const isoActionAvailable = (record) => + record.hypervisor !== 'External' && ['Running', 'Stopped'].includes(record.state) && record.vmtype !== 'sharedfsvm' export default { name: 'compute', @@ -289,6 +301,21 @@ export default { } } }, + { + api: 'finishBackupChain', + icon: 'vertical-align-middle-outlined', + label: 'label.backup.chain.finish', + dataView: true, + args: ['virtualmachineid'], + show: (record) => { + return ['Running', 'Stopped', 'BackupError'].includes(record.state) && record.backupofferingid && record.backupprovider === 'kboss' + }, + mapping: { + virtualmachineid: { + value: (record, params) => { return record.id } + } + } + }, { api: 'attachIso', icon: 'paper-clip-outlined', @@ -296,7 +323,7 @@ export default { docHelp: 'adminguide/templates.html#attaching-an-iso-to-a-vm', dataView: true, popup: true, - show: (record) => { return record.hypervisor !== 'External' && ['Running', 'Stopped'].includes(record.state) && !record.isoid && record.vmtype !== 'sharedfsvm' }, + show: (record) => isoActionAvailable(record) && attachedIsoCount(record) < isoMaxCount(record), disabled: (record) => { return record.hostcontrolstate === 'Offline' || record.hostcontrolstate === 'Maintenance' }, component: shallowRef(defineAsyncComponent(() => import('@/views/compute/AttachIso.vue'))) }, @@ -304,22 +331,11 @@ export default { api: 'detachIso', icon: 'link-outlined', label: 'label.action.detach.iso', - message: 'message.detach.iso.confirm', dataView: true, - args: (record, store) => { - var args = ['virtualmachineid'] - if (record && record.hypervisor && record.hypervisor === 'VMware') { - args.push('forced') - } - return args - }, - show: (record) => { return record.hypervisor !== 'External' && ['Running', 'Stopped'].includes(record.state) && 'isoid' in record && record.isoid && record.vmtype !== 'sharedfsvm' }, + popup: true, + show: (record) => isoActionAvailable(record) && attachedIsoCount(record) > 0, disabled: (record) => { return record.hostcontrolstate === 'Offline' || record.hostcontrolstate === 'Maintenance' }, - mapping: { - virtualmachineid: { - value: (record, params) => { return record.id } - } - } + component: shallowRef(defineAsyncComponent(() => import('@/views/compute/DetachIso.vue'))) }, { api: 'updateVMAffinityGroup', @@ -328,7 +344,7 @@ export default { docHelp: 'adminguide/virtual_machines.html#change-affinity-group-for-an-existing-vm', dataView: true, args: ['affinitygroupids'], - show: (record) => { return record.hypervisor !== 'External' && ['Stopped'].includes(record.state) && record.vmtype !== 'sharedfsvm' }, + show: (record) => { return record.hypervisor !== 'External' && ['Stopped'].includes(record.state) && record.vmtype !== 'sharedfsvm' && record.vmtype !== 'cksnode' }, component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ChangeAffinity'))), popup: true }, @@ -533,7 +549,12 @@ export default { api: 'deleteVMSnapshot', icon: 'delete-outlined', label: 'label.action.vmsnapshot.delete', - message: 'message.action.vmsnapshot.delete', + message: (record) => { + if (record.hypervisor !== 'KVM' || record.type === 'Disk') { + return 'message.action.vmsnapshot.disk-only.delete' + } + return 'message.action.vmsnapshot.delete' + }, dataView: true, show: (record) => { return ['Ready', 'Expunging', 'Error'].includes(record.state) }, args: ['vmsnapshotid'], @@ -551,7 +572,7 @@ export default { { name: 'kubernetes', title: 'label.kubernetes', - icon: ['fa-solid', 'fa-dharmachakra'], + icon: kubernetesIcon, docHelp: 'plugins/cloudstack-kubernetes-service.html', searchFilters: ['name', 'domainid', 'account', 'state'], permission: ['listKubernetesClusters'], @@ -573,7 +594,7 @@ export default { const filters = ['cloud.managed', 'external.managed'] return filters }, - details: ['name', 'description', 'zonename', 'kubernetesversionname', 'autoscalingenabled', 'minsize', 'maxsize', 'size', 'controlnodes', 'etcdnodes', 'cpunumber', 'memory', 'keypair', 'cniconfigname', 'associatednetworkname', 'account', 'domain', 'zonename', 'clustertype', 'created'], + details: ['name', 'description', 'zonename', 'kubernetesversionname', 'autoscalingenabled', 'csienabled', 'minsize', 'maxsize', 'size', 'controlnodes', 'controlaffinitygroupnames', 'etcdnodes', 'etcdaffinitygroupnames', 'workeraffinitygroupnames', 'cpunumber', 'memory', 'keypair', 'cniconfigname', 'associatednetworkname', 'account', 'domain', 'zonename', 'clustertype', 'created'], tabs: [ { name: 'k8s', @@ -581,6 +602,182 @@ export default { } ], resourceType: 'KubernetesCluster', + advisories: [ + { + id: 'cks-min-offering', + severity: 'warning', + message: 'message.advisory.cks.min.offering', + docsHelp: 'plugins/cloudstack-kubernetes-service.html', + dismissOnConditionFail: true, + condition: async (store) => { + if (!('listServiceOfferings' in store.getters.apis)) { + return false + } + const params = { + cpunumber: 2, + memory: 2048, + issystem: false + } + try { + const json = await getAPI('listServiceOfferings', params) + const offerings = json?.listserviceofferingsresponse?.serviceoffering || [] + return !offerings.some(o => !o.iscustomized) + } catch (error) {} + return false + }, + actions: [ + { + primary: true, + label: 'label.add.minimum.required.compute.offering', + loadingLabel: 'message.adding.minimum.required.compute.offering.kubernetes.cluster', + show: (store) => { return ('createServiceOffering' in store.getters.apis) }, + run: async () => { + const params = { + name: 'CKS Instance', + cpunumber: 2, + cpuspeed: 1000, + memory: 2048, + iscustomized: false, + issystem: false + } + try { + const json = await postAPI('createServiceOffering', params) + if (json?.createserviceofferingresponse?.serviceoffering) { + return true + } + } catch (error) {} + return false + }, + successMessage: 'message.added.minimum.required.compute.offering.kubernetes.cluster', + errorMessage: 'message.add.minimum.required.compute.offering.kubernetes.cluster.failed' + }, + { + label: 'label.go.to.compute.offerings', + show: (store) => { return ('listServiceOfferings' in store.getters.apis) }, + run: (store, router) => { + router.push({ name: 'computeoffering' }) + return false + } + } + ] + }, + { + id: 'cks-version-check', + severity: 'warning', + message: 'message.advisory.cks.version.check', + docsHelp: 'plugins/cloudstack-kubernetes-service.html', + dismissOnConditionFail: true, + condition: async (store) => { + const api = 'listKubernetesSupportedVersions' + if (!(api in store.getters.apis)) { + return false + } + try { + const json = await getAPI(api, {}) + const versions = json?.listkubernetessupportedversionsresponse?.kubernetessupportedversion || [] + return versions.length === 0 + } catch (error) {} + return false + }, + actions: [ + { + primary: true, + label: 'label.add.latest.kubernetes.iso', + loadingLabel: 'message.adding.latest.kubernetes.iso', + show: (store) => { return ('addKubernetesSupportedVersion' in store.getters.apis) }, + run: async () => { + let arch = 'x86_64' + if ('listClusters' in store.getters.apis) { + try { + const json = await getAPI('listClusters', { allocationstate: 'Enabled', page: 1, pagesize: 1 }) + const cluster = json?.listclustersresponse?.cluster?.[0] || {} + arch = cluster.architecture || 'x86_64' + } catch (error) {} + } + const params = await getLatestKubernetesIsoParams(arch) + try { + const json = await postAPI('addKubernetesSupportedVersion', params) + if (json?.addkubernetessupportedversionresponse?.kubernetessupportedversion) { + return true + } + } catch (error) {} + return false + }, + successMessage: 'message.added.latest.kubernetes.iso', + errorMessage: 'message.add.latest.kubernetes.iso.failed' + }, + { + label: 'label.go.to.kubernetes.isos', + show: true, + run: (store, router) => { + router.push({ name: 'kubernetesiso' }) + return false + } + } + ] + }, + { + id: 'cks-endpoint-url', + severity: 'warning', + message: 'message.advisory.cks.endpoint.url.not.configured', + docsHelp: 'plugins/cloudstack-kubernetes-service.html', + dismissOnConditionFail: true, + condition: async (store) => { + if (!['Admin'].includes(store.getters.userInfo.roletype)) { + return false + } + let url = '' + const baseUrl = getBaseUrl() + if (baseUrl.startsWith('/')) { + url = window.location.origin + baseUrl + } + if (!url || url.startsWith('http://localhost')) { + return false + } + const params = { + name: 'endpoint.url' + } + const json = await getAPI('listConfigurations', params) + const configuration = json?.listconfigurationsresponse?.configuration?.[0] || {} + return !configuration.value || configuration.value.startsWith('http://localhost') + }, + actions: [ + { + primary: true, + label: 'label.fix.global.setting', + show: (store) => { return ('updateConfiguration' in store.getters.apis) }, + run: async () => { + let url = '' + const baseUrl = getBaseUrl() + if (baseUrl.startsWith('/')) { + url = window.location.origin + baseUrl + } + const params = { + name: 'endpoint.url', + value: url + } + try { + const json = await postAPI('updateConfiguration', params) + if (json?.updateconfigurationresponse?.configuration) { + return true + } + } catch (error) {} + return false + }, + successMessage: 'message.global.setting.updated', + errorMessage: 'message.global.setting.update.failed' + }, + { + label: 'label.go.to.global.settings', + show: (store) => { return ('listConfigurations' in store.getters.apis) }, + run: (store, router) => { + router.push({ name: 'globalsetting' }) + return false + } + } + ] + } + ], actions: [ { api: 'createKubernetesCluster', @@ -618,7 +815,7 @@ export default { }, { api: 'scaleKubernetesCluster', - icon: 'swap-outlined', + icon: 'arrows-alt-outlined', label: 'label.kubernetes.cluster.scale', message: 'message.kubernetes.cluster.scale', docHelp: 'plugins/cloudstack-kubernetes-service.html#scaling-kubernetes-cluster', @@ -627,6 +824,15 @@ export default { popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ScaleKubernetesCluster.vue'))) }, + { + api: 'updateKubernetesClusterAffinityGroups', + icon: 'swap-outlined', + label: 'label.change.affinity', + dataView: true, + show: (record) => { return ['Stopped'].includes(record.state) && record.clustertype === 'CloudManaged' }, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ChangeKubernetesClusterAffinity.vue'))) + }, { api: 'upgradeKubernetesCluster', icon: 'plus-circle-outlined', @@ -721,6 +927,12 @@ export default { name: 'scaledown.policy', component: shallowRef(defineAsyncComponent(() => import('@/views/compute/AutoScaleDownPolicyTab.vue'))) }, + { + name: 'schedules', + resourceType: 'AutoScaleVmGroup', + component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ResourceSchedules.vue'))), + show: () => { return 'listResourceSchedule' in store.getters.apis } + }, { name: 'events', resourceType: 'AutoScaleVmGroup', @@ -770,9 +982,9 @@ export default { dataView: true, args: (record, store) => { var args = ['name'] + args.push('maxmembers') + args.push('minmembers') if (record.state === 'DISABLED') { - args.push('maxmembers') - args.push('minmembers') args.push('interval') } return args @@ -1146,12 +1358,8 @@ export default { label: 'label.add.affinity.group', docHelp: 'adminguide/virtual_machines.html#creating-a-new-affinity-group', listView: true, - args: ['name', 'description', 'type'], - mapping: { - type: { - options: ['host anti-affinity (Strict)', 'host affinity (Strict)', 'host anti-affinity (Non-Strict)', 'host affinity (Non-Strict)'] - } - } + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/compute/CreateAffinityGroup.vue'))) }, { api: 'deleteAffinityGroup', diff --git a/ui/src/config/section/config.js b/ui/src/config/section/config.js index 8f70104eaf10..a2c12ce236ac 100644 --- a/ui/src/config/section/config.js +++ b/ui/src/config/section/config.js @@ -41,7 +41,7 @@ export default { permission: ['listLdapConfigurations'], searchFilters: ['domainid', 'hostname', 'port'], columns: ['hostname', 'port', 'domainid'], - details: ['hostname', 'port', 'domainid'], + details: ['id', 'hostname', 'port', 'domainid'], actions: [ { api: 'addLdapConfiguration', @@ -79,8 +79,8 @@ export default { icon: 'login-outlined', docHelp: 'adminguide/accounts.html#using-an-ldap-server-for-user-authentication', permission: ['listOauthProvider'], - columns: ['provider', 'enabled', 'description', 'clientid', 'secretkey', 'redirecturi'], - details: ['provider', 'description', 'enabled', 'clientid', 'secretkey', 'redirecturi'], + columns: ['provider', 'enabled', 'description', 'clientid', 'redirecturi', 'domainpath'], + details: ['provider', 'description', 'enabled', 'clientid', 'secretkey', 'redirecturi', 'authorizeurl', 'tokenurl', 'domainpath'], actions: [ { api: 'registerOauthProvider', @@ -89,11 +89,11 @@ export default { listView: true, dataView: false, args: [ - 'provider', 'description', 'clientid', 'redirecturi', 'secretkey' + 'provider', 'description', 'clientid', 'redirecturi', 'secretkey', 'authorizeurl', 'tokenurl', 'domainid' ], mapping: { provider: { - options: ['google', 'github'] + options: ['google', 'github', 'keycloak'] } } }, @@ -103,7 +103,7 @@ export default { label: 'label.edit', dataView: true, popup: true, - args: ['description', 'clientid', 'redirecturi', 'secretkey'] + args: ['description', 'clientid', 'redirecturi', 'secretkey', 'authorizeurl', 'tokenurl', 'domainid'] }, { api: 'updateOauthProvider', @@ -133,43 +133,6 @@ export default { } ] }, - { - name: 'backuprepository', - title: 'label.backup.repository', - icon: 'inbox-outlined', - docHelp: 'adminguide/backup_and_recovery.html', - permission: ['listBackupRepositories'], - searchFilters: ['zoneid'], - columns: ['name', 'provider', 'type', 'address', 'zonename'], - details: ['name', 'type', 'address', 'provider', 'zonename'], - actions: [ - { - api: 'addBackupRepository', - icon: 'plus-outlined', - label: 'label.backup.repository.add', - listView: true, - args: [ - 'name', 'provider', 'address', 'type', 'mountopts', 'zoneid' - ], - mapping: { - type: { - options: ['nfs', 'cifs', 'ceph'] - }, - provider: { - value: (record) => { return 'nas' } - } - } - }, - { - api: 'deleteBackupRepository', - icon: 'delete-outlined', - label: 'label.backup.repository.remove', - message: 'message.action.delete.backup.repository', - dataView: true, - popup: true - } - ] - }, { name: 'hypervisorcapability', title: 'label.hypervisor.capabilities', diff --git a/ui/src/config/section/domain.js b/ui/src/config/section/domain.js index fbe20ef8891b..706cbf805cfa 100644 --- a/ui/src/config/section/domain.js +++ b/ui/src/config/section/domain.js @@ -144,7 +144,7 @@ export default { docHelp: 'adminguide/accounts.html#using-an-ldap-server-for-user-authentication', listView: true, dataView: true, - args: ['type', 'domainid', 'name', 'accounttype', 'admin'], + args: ['type', 'domainid', 'ldapdomain', 'accounttype', 'admin'], mapping: { type: { options: ['GROUP', 'OU'] @@ -157,6 +157,20 @@ export default { } } }, + { + api: 'unlinkDomainFromLdap', + icon: 'ArrowsAltOutlined', + label: 'label.unlink.domain.from.ldap', + docHelp: 'adminguide/accounts.html#using-an-ldap-server-for-user-authentication', + listView: true, + dataView: true, + args: ['domainid'], + mapping: { + domainid: { + value: (record) => { return record.id } + } + } + }, { api: 'deleteDomain', icon: 'delete-outlined', diff --git a/ui/src/config/section/extension.js b/ui/src/config/section/extension.js index 4c6d9ebf0761..8bb3ee362f60 100644 --- a/ui/src/config/section/extension.js +++ b/ui/src/config/section/extension.js @@ -44,8 +44,8 @@ export default { }, 'created'] return fields }, - details: ['name', 'description', 'id', 'type', 'details', 'path', 'pathready', 'isuserdefined', 'orchestratorrequirespreparevm', 'created'], - filters: ['orchestrator'], + details: ['name', 'description', 'id', 'type', 'details', 'path', 'pathready', 'isuserdefined', 'orchestratorrequirespreparevm', 'reservedresourcedetails', 'created'], + filters: ['Orchestrator', 'NetworkOrchestrator'], tabs: [{ name: 'details', component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) diff --git a/ui/src/config/section/image.js b/ui/src/config/section/image.js index f0458088b24f..bae4a40c9a8d 100644 --- a/ui/src/config/section/image.js +++ b/ui/src/config/section/image.js @@ -18,6 +18,7 @@ import { shallowRef, defineAsyncComponent } from 'vue' import store from '@/store' import { isZoneCreated } from '@/utils/zone' +import kubernetesIcon from '@/assets/icons/kubernetes.svg?inline' export default { name: 'image', @@ -60,9 +61,9 @@ export default { details: () => { var fields = ['name', 'id', 'displaytext', 'checksum', 'hypervisor', 'arch', 'format', 'externalprovisioner', 'ostypename', 'size', 'physicalsize', 'isready', 'passwordenabled', 'crossZones', 'templatetype', 'directdownload', 'deployasis', 'ispublic', 'isfeatured', 'isextractable', 'isdynamicallyscalable', 'crosszones', 'type', - 'account', 'domain', 'created', 'userdatadetails', 'userdatapolicy', 'forcks'] + 'account', 'domain', 'created', 'userdatadetails', 'userdatapolicy', 'url', 'forcks'] if (['Admin'].includes(store.getters.userInfo.roletype)) { - fields.push('templatetag', 'templatetype', 'url') + fields.push('templatetag', 'templatetype') } return fields }, @@ -367,12 +368,12 @@ export default { { name: 'kubernetesiso', title: 'label.kubernetes.isos', - icon: ['fa-solid', 'fa-dharmachakra'], + icon: kubernetesIcon, docHelp: 'plugins/cloudstack-kubernetes-service.html#kubernetes-supported-versions', permission: ['listKubernetesSupportedVersions'], searchFilters: ['zoneid', 'minimumsemanticversion', 'arch'], columns: ['name', 'state', 'semanticversion', 'isostate', 'mincpunumber', 'minmemory', 'arch', 'zonename'], - details: ['name', 'semanticversion', 'supportsautoscaling', 'zoneid', 'zonename', 'isoid', 'isoname', 'isostate', 'arch', 'mincpunumber', 'minmemory', 'supportsha', 'state', 'created'], + details: ['name', 'semanticversion', 'supportsautoscaling', 'zoneid', 'zonename', 'isoid', 'isoname', 'isostate', 'arch', 'mincpunumber', 'minmemory', 'supportsha', 'state', 'created', 'isourl'], tabs: [ { name: 'details', @@ -395,6 +396,15 @@ export default { show: isZoneCreated, component: shallowRef(defineAsyncComponent(() => import('@/views/image/AddKubernetesSupportedVersion.vue'))) }, + { + api: 'getUploadParamsForKubernetesSupportedVersion', + icon: 'cloud-upload-outlined', + label: 'label.kubernetes.version.from.local', + listView: true, + popup: true, + show: isZoneCreated, + component: shallowRef(defineAsyncComponent(() => import('@/views/image/AddKubernetesSupportedVersion.vue'))) + }, { api: 'updateKubernetesSupportedVersion', icon: 'edit-outlined', diff --git a/ui/src/config/section/infra.js b/ui/src/config/section/infra.js index ae074002834d..dc365b74c930 100644 --- a/ui/src/config/section/infra.js +++ b/ui/src/config/section/infra.js @@ -23,6 +23,7 @@ import clusters from '@/config/section/infra/clusters' import hosts from '@/config/section/infra/hosts' import primaryStorages from '@/config/section/infra/primaryStorages' import secondaryStorages from '@/config/section/infra/secondaryStorages' +import backupRepositories from '@/config/section/infra/backupRepositories' import objectStorages from '@/config/section/infra/objectStorages' import systemVms from '@/config/section/infra/systemVms' import routers from '@/config/section/infra/routers' @@ -50,6 +51,7 @@ export default { hosts, primaryStorages, secondaryStorages, + backupRepositories, objectStorages, systemVms, routers, diff --git a/ui/src/config/section/infra/backupRepositories.js b/ui/src/config/section/infra/backupRepositories.js new file mode 100644 index 000000000000..aa1aa5eab4fe --- /dev/null +++ b/ui/src/config/section/infra/backupRepositories.js @@ -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. + +export default { + name: 'backuprepository', + title: 'label.backup.repository', + icon: 'inbox-outlined', + docHelp: 'adminguide/nas_plugin.html', + permission: ['listBackupRepositories'], + searchFilters: ['zoneid'], + columns: ['name', 'provider', 'type', 'address', 'zonename'], + details: ['name', 'type', 'address', 'provider', 'zonename', 'crosszoneinstancecreation'], + actions: [ + { + api: 'addBackupRepository', + icon: 'plus-outlined', + label: 'label.backup.repository.add', + listView: true, + args: [ + 'name', 'provider', 'address', 'type', 'mountopts', 'zoneid', 'crosszoneinstancecreation' + ], + mapping: { + type: { + options: ['nfs', 'cifs', 'ceph'] + }, + provider: { + value: (record) => { return 'nas' } + } + } + }, + { + api: 'updateBackupRepository', + icon: 'edit-outlined', + label: 'label.backup.repository.edit', + message: 'message.action.edit.backup.repository', + args: ['name', 'address', 'mountopts', 'crosszoneinstancecreation'], + dataView: true, + popup: true + }, + { + api: 'deleteBackupRepository', + icon: 'delete-outlined', + label: 'label.backup.repository.remove', + message: 'message.action.delete.backup.repository', + dataView: true, + popup: true + } + ] +} diff --git a/ui/src/config/section/infra/hosts.js b/ui/src/config/section/infra/hosts.js index 2f27db5780bc..48e850a22fbe 100644 --- a/ui/src/config/section/infra/hosts.js +++ b/ui/src/config/section/infra/hosts.js @@ -103,7 +103,7 @@ export default { show: (record) => { return record.hypervisor === 'KVM' || record.hypervisor === store.getters.customHypervisorName }, - args: ['hostid'], + args: ['hostid', 'forced'], mapping: { hostid: { value: (record) => { return record.id } diff --git a/ui/src/config/section/infra/managementServers.js b/ui/src/config/section/infra/managementServers.js index d2d11d5b25d0..4bf54943fd85 100644 --- a/ui/src/config/section/infra/managementServers.js +++ b/ui/src/config/section/infra/managementServers.js @@ -39,6 +39,10 @@ export default { name: 'details', component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) }, + { + name: 'settings', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/SettingsTab.vue'))) + }, { name: 'management.server.peers', component: shallowRef(defineAsyncComponent(() => import('@/views/infra/ManagementServerPeerTab.vue'))) diff --git a/ui/src/config/section/infra/phynetworks.js b/ui/src/config/section/infra/phynetworks.js index 0863eff6ec0b..9b7a0a83f7f3 100644 --- a/ui/src/config/section/infra/phynetworks.js +++ b/ui/src/config/section/infra/phynetworks.js @@ -94,7 +94,7 @@ export default { icon: 'edit-outlined', label: 'label.update.physical.network', dataView: true, - args: ['vlan', 'tags'] + args: ['vlan', 'tags', 'externaldetails'] }, { api: 'addTrafficType', diff --git a/ui/src/config/section/infra/systemVms.js b/ui/src/config/section/infra/systemVms.js index 6e135ccdd36a..4a5879b17626 100644 --- a/ui/src/config/section/infra/systemVms.js +++ b/ui/src/config/section/infra/systemVms.js @@ -26,7 +26,7 @@ export default { permission: ['listSystemVms'], searchFilters: ['name', 'zoneid', 'podid', 'hostid', 'systemvmtype', 'storageid', 'arch'], columns: ['name', 'state', 'agentstate', 'systemvmtype', 'publicip', 'privateip', 'linklocalip', 'version', 'hostname', 'arch', 'zonename'], - details: ['name', 'id', 'agentstate', 'systemvmtype', 'publicip', 'privateip', 'linklocalip', 'gateway', 'hostname', 'arch', 'version', 'zonename', 'created', 'activeviewersessions', 'isdynamicallyscalable', 'hostcontrolstate'], + details: ['name', 'id', 'agentstate', 'systemvmtype', 'publicip', 'privateip', 'linklocalip', 'gateway', 'hostname', 'arch', 'version', 'zonename', 'created', 'activeviewersessions', 'isdynamicallyscalable', 'hostcontrolstate', 'storageip'], resourceType: 'SystemVm', filters: () => { const filters = ['starting', 'running', 'stopping', 'stopped', 'destroyed', 'expunging', 'migrating', 'error', 'unknown', 'shutdown'] diff --git a/ui/src/config/section/keypair.js b/ui/src/config/section/keypair.js new file mode 100644 index 000000000000..86486db19c38 --- /dev/null +++ b/ui/src/config/section/keypair.js @@ -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. + +import { shallowRef, defineAsyncComponent } from 'vue' +import store from '@/store' + +export default { + name: 'keypair', + identifier: 'keypairid', + title: 'label.apikeypairs', + icon: 'key-outlined', + hidden: true, + docHelp: 'adminguide/accounts.html#keypairs', + permission: ['listUserKeys'], + columns: [ + 'name', + { field: 'startdate', customTitle: 'apikeypair.startdate' }, + { field: 'enddate', customTitle: 'apikeypair.enddate' }, + 'username', 'rolename' + ], + details: [ + 'id', 'name', 'description', + 'domain', 'role', 'roletype', + { field: 'accountname', customTitle: 'account' }, 'username', + { field: 'startdate', customTitle: 'apikeypair.startdate' }, + { field: 'enddate', customTitle: 'apikeypair.enddate' }, + 'created' + ], + tabs: [{ + name: 'details', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) + }, { + name: 'rules', + component: shallowRef(defineAsyncComponent(() => import('@/views/iam/ApiKeyPairPermissionTable.vue'))), + show: () => { return 'listUserKeyRules' in store.getters.apis } + }], + actions: [ + { + api: 'deleteUserKeys', + icon: 'delete-outlined', + label: 'label.action.delete.keypair', + message: 'message.delete.keypair', + dataView: true, + args: ['keypairid'], + mapping: { + keypairid: { + value: (record) => { return record.id } + } + }, + show: () => { + return 'deleteUserKeys' in store.getters.apis + } + } + ] +} diff --git a/ui/src/config/section/kms.js b/ui/src/config/section/kms.js new file mode 100644 index 000000000000..3120fee54cc8 --- /dev/null +++ b/ui/src/config/section/kms.js @@ -0,0 +1,280 @@ +// 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 { shallowRef, defineAsyncComponent } from 'vue' +import store from '@/store' + +export default { + name: 'kms', + title: 'label.kms', + icon: 'lock-outlined', + show: () => { + return ['Admin'].includes(store.getters.userInfo.roletype) || store.getters.features.hashsmprofiles + }, + children: [ + { + name: 'kmskey', + title: 'label.kms.keys', + icon: 'file-text-outlined', + permission: ['listKMSKeys'], + resourceType: 'KMSKey', + columns: () => { + const fields = ['name', 'enabled', 'hsmprofile'] + if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { + fields.push('account') + } + if (store.getters.listAllProjects) { + fields.push('project') + } + fields.push('domainpath') + return fields + }, + details: () => { + const fields = ['id', 'name', 'description', 'version'] + if (['Admin'].includes(store.getters.userInfo.roletype)) { + fields.push('keklabel') + } + fields.push('keybits', 'enabled', 'account', 'domainpath', 'project', 'created', 'hsmprofile') + return fields + }, + related: [ + { + name: 'volume', + title: 'label.volumes', + param: 'kmskeyid' + } + ], + tabs: [ + { + name: 'details', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) + }, + { + name: 'events', + resourceType: 'KmsKey', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/EventsTab.vue'))), + show: () => { + return 'listEvents' in store.getters.apis + } + } + ], + searchFilters: () => { + var filters = ['zoneid', 'hsmprofileid'] + if (store.getters.userInfo.roletype === 'Admin') { + filters.push('domainid', 'account', 'projectid') + } + return filters + }, + actions: [ + { + api: 'createKMSKey', + icon: 'plus-outlined', + label: 'label.create.kms.key', + docHelp: 'adminguide/kms.html#creating-a-kms-key', + listView: true, + popup: true, + dataView: false, + args: (record, store, group) => { + return ['Admin'].includes(store.userInfo.roletype) + ? ['zoneid', 'domainid', 'account', 'projectid', 'name', 'description', 'hsmprofileid', 'keybits'] + : ['zoneid', 'name', 'description', 'hsmprofileid', 'keybits'] + }, + mapping: { + hsmprofileid: { + api: 'listHSMProfiles', + params: (record) => { return { enabled: true } } + } + } + }, + { + api: 'updateKMSKey', + icon: 'edit-outlined', + label: 'label.update.kms.key', + dataView: true, + popup: true, + args: ['id', 'name', 'description', 'enabled'], + mapping: { + id: { + value: (record) => record.id + } + } + }, + { + api: 'rotateKMSKey', + icon: 'sync-outlined', + docHelp: 'adminguide/kms.html#rotating-a-kms-key', + label: 'label.rotate.kms.key', + dataView: true, + popup: true, + args: ['id', 'keybits', 'hsmprofileid'], + mapping: { + id: { + value: (record) => record.id + } + } + }, + { + api: 'migrateVolumesToKMS', + icon: 'swap-outlined', + docHelp: 'adminguide/kms.html#migrating-existing-volumes-to-kms', + label: 'label.migrate.volumes.to.kms', + message: 'message.action.migrate.volumes.to.kms', + dataView: true, + popup: true, + show: (record, store) => { + return ['Admin'].includes(store.userInfo.roletype) + }, + args: (record, store) => { + var fields = ['domainid', 'account', 'kmskeyid', 'volumeids'] + if (!['Admin'].includes(store.userInfo.roletype)) { + fields = ['kmskeyid', 'volumeids'] + } + return fields + }, + mapping: { + kmskeyid: { + value: (record) => record.id + }, + volumeids: { + api: 'listVolumes', + params: (record) => { return { account: record.account, domainid: record.domainid, zoneid: record.zoneid } } + } + } + }, + { + api: 'deleteKMSKey', + icon: 'delete-outlined', + label: 'label.delete.kms.key', + message: 'message.action.delete.kms.key', + dataView: true, + popup: true, + args: ['id'], + mapping: { + id: { + value: (record) => record.id + } + } + } + ] + }, + { + name: 'hsmprofile', + title: 'label.hsm.profile', + icon: 'safety-outlined', + permission: ['listHSMProfiles'], + show: () => { return ['Admin'].includes(store.getters.userInfo.roletype) }, + resourceType: 'HSMProfile', + columns: () => { + const fields = ['name', 'enabled', 'ispublic'] + if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { + fields.push('account') + } + if (store.getters.listAllProjects) { + fields.push('project') + } + fields.push('domainpath') + return fields + }, + details: ['id', 'name', 'description', 'protocol', 'enabled', 'ispublic', 'account', 'domainpath', 'project', 'created', 'details'], + related: [ + { + name: 'kmskey', + title: 'label.kms.keys', + param: 'hsmprofileid' + } + ], + tabs: [ + { + name: 'details', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) + }, + { + name: 'events', + resourceType: 'HsmProfile', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/EventsTab.vue'))), + show: () => { + return 'listEvents' in store.getters.apis + } + } + ], + searchFilters: () => { + var filters = ['zoneid'] + if (store.getters.userInfo.roletype === 'Admin') { + filters.push('domainid', 'account', 'projectid') + } + return filters + }, + actions: [ + { + api: 'createHSMProfile', + icon: 'plus-outlined', + docHelp: 'adminguide/kms.html#adding-an-hsm-profile', + label: 'label.create.hsmprofile', + listView: true, + popup: true, + dataView: false, + show: (record, store) => { + return ['Admin'].includes(store.userInfo.roletype) + }, + args: (record, store, group) => { + return ['Admin'].includes(store.userInfo.roletype) + ? ['name', 'zoneid', 'vendorname', 'domainid', 'account', 'projectid', 'details', 'ispublic'] + : ['name', 'zoneid', 'vendorname', 'details'] + }, + mapping: { + details: { + optionalKeys: ['pin', 'library', 'slot', 'slot_list_index', 'token_label'] + } + } + }, + { + api: 'updateHSMProfile', + icon: 'edit-outlined', + label: 'label.update.hsm.profile', + dataView: true, + popup: true, + show: (record, store) => { + return ['Admin'].includes(store.userInfo.roletype) + }, + args: ['id', 'name', 'enabled'], + mapping: { + id: { + value: (record) => record.id + } + } + }, + { + api: 'deleteHSMProfile', + icon: 'delete-outlined', + label: 'label.delete.hsm.profile', + message: 'message.action.delete.hsm.profile', + dataView: true, + popup: true, + show: (record, store) => { + return ['Admin'].includes(store.userInfo.roletype) + }, + args: ['id'], + mapping: { + id: { + value: (record) => record.id + } + } + } + ] + } + ] +} diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js index 8c1ffcdf9cd4..30608a82a98c 100644 --- a/ui/src/config/section/network.js +++ b/ui/src/config/section/network.js @@ -18,8 +18,9 @@ import { shallowRef, defineAsyncComponent } from 'vue' import store from '@/store' import tungsten from '@/assets/icons/tungsten.svg?inline' -import { isAdmin } from '@/role' +import { isAdmin, isAdminOrDomainAdmin } from '@/role' import { isZoneCreated } from '@/utils/zone' +import { vueProps } from '@/vue-app' export default { name: 'network', @@ -48,9 +49,17 @@ export default { return fields }, details: () => { - var fields = ['name', 'id', 'description', 'type', 'traffictype', 'vpcid', 'vlan', 'broadcasturi', 'cidr', 'ip6cidr', 'netmask', 'gateway', 'asnumber', 'aclname', 'ispersistent', 'restartrequired', 'reservediprange', 'redundantrouter', 'networkdomain', 'egressdefaultpolicy', 'zonename', 'account', 'domainpath', 'associatednetwork', 'associatednetworkid', 'ip4routing', 'ip6firewall', 'ip6routing', 'ip6routes', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu', 'privatemtu'] - if (!isAdmin()) { - fields = fields.filter(function (e) { return e !== 'broadcasturi' }) + const fields = ['name', 'id', 'description', 'type', 'traffictype', 'vpcid', 'vlan', 'cidr', 'ip6cidr', 'netmask', 'gateway', 'asnumber', + 'aclname', 'ispersistent', 'restartrequired', 'reservediprange', 'redundantrouter', 'networkdomain', 'egressdefaultpolicy', 'zonename', + 'account', 'domainpath', 'associatednetwork', 'associatednetworkid', 'ip4routing', 'ip6firewall', 'ip6routing', 'ip6routes', + 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu', 'privatemtu', 'dnszone', 'dnssubdomain'] + if (isAdmin()) { + const vlanIndex = fields.findIndex(detail => detail === 'vlan') + fields.splice(vlanIndex + 1, 0, 'broadcasturi') + fields.push({ + field: 'keepmacaddressonpublicnic', + customTitle: 'keep.mac.address.on.public.nic' + }) } return fields }, @@ -112,6 +121,14 @@ export default { name: 'network.permissions', component: shallowRef(defineAsyncComponent(() => import('@/views/network/NetworkPermissions.vue'))), show: (record, route, user) => { return 'listNetworkPermissions' in store.getters.apis && record.acltype === 'Account' && !('vpcid' in record) && (['Admin', 'DomainAdmin'].includes(user.roletype) || record.account === user.account) && !record.projectid } + }, { + name: 'custom.actions', + component: shallowRef(defineAsyncComponent(() => import('@/views/extension/RunCustomAction.vue'))), + show: (record) => { + return 'runCustomAction' in store.getters.apis && + 'listCustomActions' in store.getters.apis && + record.service && record.service.some(s => s.name === 'CustomAction') + } }, { name: 'events', @@ -171,13 +188,16 @@ export default { if (isGroupAction || record.vpcid == null) { fields.push('cleanup') } + if (!record.redundantrouter && vueProps.$config.allowMakingRouterRedundant) { + fields.push('makeredundant') + } fields.push('livepatch') return fields }, show: (record) => record.type !== 'L2', groupAction: true, popup: true, - groupMap: (selection, values) => { return selection.map(x => { return { id: x, cleanup: values.cleanup } }) } + groupMap: (selection, values) => { return selection.map(x => { return { id: x, cleanup: values.cleanup, makeredundant: values.makeredundant } }) } }, { api: 'replaceNetworkACLList', @@ -198,6 +218,49 @@ export default { } } }, + { + api: 'runCustomAction', + icon: 'thunderbolt-outlined', + label: 'label.run.custom.action', + dataView: true, + show: (record) => { + return 'runCustomAction' in store.getters.apis && + 'listCustomActions' in store.getters.apis && + record.service && record.service.some(s => s.name === 'CustomAction') + }, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/extension/RunCustomAction.vue'))) + }, + { + api: 'associateDnsZoneToNetwork', + icon: 'link-outlined', + label: 'label.action.associate.dns.zone', + dataView: true, + show: (record, store) => { + return (record.type === 'Shared' && record.dnszone === undefined && + (record.account === store.userInfo.account || isAdminOrDomainAdmin(store.userInfo.roletype))) + }, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/network/dns/AssociateDnsZone.vue'))) + }, + { + api: 'disassociateDnsZoneFromNetwork', + icon: 'disconnect-outlined', + label: 'label.action.disassociate.dns.zone', + message: 'message.action.disassociate.dns.zone', + dataView: true, + popup: true, + args: ['networkid'], + show: (record, store) => { + return record.dnszone !== undefined && record.type === 'Shared' && + (record.account === store.userInfo.account || isAdminOrDomainAdmin(store.userInfo.roletype)) + }, + mapping: { + networkid: { + value: (record) => { return record.id } + } + } + }, { api: 'deleteNetwork', icon: 'delete-outlined', @@ -229,7 +292,16 @@ export default { fields.push(...['domain', 'zonename']) return fields }, - details: ['name', 'id', 'displaytext', 'cidr', 'networkdomain', 'ip4routing', 'ip4routes', 'ip6routes', 'ispersistent', 'redundantvpcrouter', 'restartrequired', 'zonename', 'account', 'domain', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu'], + details: () => { + const fields = ['name', 'id', 'displaytext', 'cidr', 'networkdomain', 'ip4routing', 'ip4routes', 'ip6routes', 'ispersistent', 'redundantvpcrouter', 'restartrequired', 'zonename', 'account', 'domain', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu'] + if (isAdmin()) { + fields.push({ + field: 'keepmacaddressonpublicnic', + customTitle: 'keep.mac.address.on.public.nic' + }) + } + return fields + }, searchFilters: ['name', 'zoneid', 'domainid', 'account', 'restartrequired', 'tags'], related: [{ name: 'vm', @@ -264,7 +336,13 @@ export default { icon: 'edit-outlined', label: 'label.edit', dataView: true, - args: ['name', 'displaytext', 'publicmtu', 'sourcenatipaddress'] + args: () => { + const fields = ['name', 'displaytext', 'publicmtu', 'sourcenatipaddress'] + if (isAdmin()) { + fields.push('keepmacaddressonpublicnic') + } + return fields + } }, { api: 'restartVPC', @@ -356,7 +434,10 @@ export default { permission: ['listVnfAppliances'], resourceType: 'UserVm', params: () => { - return { details: 'servoff,tmpl,nics', isvnf: true } + return { + details: 'group,nics,secgrp,tmpl,servoff,diskoff,iso,volume,affgrp,backoff,vnfnics', + isvnf: true + } }, columns: () => { const fields = ['name', 'state', 'ipaddress'] @@ -1263,15 +1344,11 @@ export default { { api: 'updateVpnCustomerGateway', icon: 'edit-outlined', - label: 'label.edit', + label: 'label.update.vpn.customer.gateway', docHelp: 'adminguide/networking_and_traffic.html#updating-and-removing-a-vpn-customer-gateway', dataView: true, - args: ['name', 'gateway', 'cidrlist', 'ipsecpsk', 'ikepolicy', 'ikelifetime', 'ikeversion', 'esppolicy', 'esplifetime', 'dpd', 'splitconnections', 'forceencap'], - mapping: { - ikeversion: { - options: ['ike', 'ikev1', 'ikev2'] - } - } + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/network/UpdateVpnCustomerGateway.vue'))) }, { api: 'deleteVpnCustomerGateway', @@ -1488,6 +1565,103 @@ export default { groupMap: (selection) => { return selection.map(x => { return { id: x } }) } } ] + }, + { + name: 'dnszone', + title: 'label.dns.zones', + icon: 'apartment-outlined', + permission: ['listDnsZones'], + columns: ['name', 'state', 'dnsservername', 'account', 'description'], + details: ['name', 'id', 'state', 'dnsservername', 'dnsserverid', 'account', 'domainpath', 'description'], + tabs: [{ + name: 'details', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) + }, + { + name: 'dns.records', + component: shallowRef(defineAsyncComponent(() => import('@/views/network/dns/DnsRecordsTab.vue'))), + show: () => true + }], + actions: [ + { + api: 'createDnsZone', + icon: 'plus-outlined', + label: 'label.dns.create.zone', + listView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/network/dns/CreateDnsZone.vue'))), + show: () => { + return true + } + }, + { + api: 'updateDnsZone', + icon: 'edit-outlined', + label: 'label.dns.update.zone', + dataView: true, + popup: true, + show: (record, store) => { return record.account === store.userInfo.account || isAdminOrDomainAdmin(store.userInfo.roletype) }, + component: shallowRef(defineAsyncComponent(() => import('@/views/network/dns/UpdateDnsZone.vue'))) + }, + { + api: 'deleteDnsZone', + icon: 'delete-outlined', + label: 'label.dns.delete.zone', + message: 'message.action.delete.dns.zone', + dataView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/network/dns/DeleteDnsZone.vue'))), + show: (record, store) => { return record.account === store.userInfo.account || isAdminOrDomainAdmin(store.userInfo.roletype) }, + groupAction: false + } + ] + }, + { + name: 'dnsserver', + title: 'label.dns.servers', + icon: 'cloud-server-outlined', + permission: ['listDnsServers'], + columns: ['name', 'url', 'provider', 'ispublic', 'port', 'nameservers', 'publicdomainsuffix'], + details: ['name', 'url', 'provider', 'ispublic', 'port', 'nameservers', 'publicdomainsuffix', 'domain', 'account'], + related: [{ + name: 'dnszone', + title: 'label.dns.zone', + param: 'dnsserverid' + }], + actions: [ + { + api: 'addDnsServer', + icon: 'plus-outlined', + label: 'label.dns.add.server', + listView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/network/dns/AddDnsServer.vue'))), + show: () => { + return true + } + }, + { + api: 'updateDnsServer', + icon: 'edit-outlined', + label: 'label.dns.update.server', + dataView: true, + popup: true, + show: (record, store) => { return record.account === store.userInfo.account || isAdminOrDomainAdmin(store.userInfo.roletype) }, + component: shallowRef(defineAsyncComponent(() => import('@/views/network/dns/UpdateDnsServer.vue'))) + }, + { + api: 'deleteDnsServer', + icon: 'delete-outlined', + label: 'label.dns.delete.server', + message: 'message.action.delete.dns.server', + dataView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/network/dns/DeleteDnsServer.vue'))), + show: (record, store) => { return record.account === store.userInfo.account || isAdminOrDomainAdmin(store.userInfo.roletype) }, + groupAction: false, + groupMap: (selection) => { return selection.map(x => { return { id: x } }) } + } + ] } ] } diff --git a/ui/src/config/section/offering.js b/ui/src/config/section/offering.js index 53dd20b2d04a..afdf2605a6a8 100644 --- a/ui/src/config/section/offering.js +++ b/ui/src/config/section/offering.js @@ -142,7 +142,15 @@ export default { } }, show: (record) => { return record.state === 'Active' }, - groupMap: (selection) => { return selection.map(x => { return { id: x } }) } + groupMap: (selection) => { return selection.map(x => { return { id: x, state: 'Inactive' } }) } + }, { + api: 'cloneServiceOffering', + icon: 'copy-outlined', + label: 'label.clone.compute.offering', + docHelp: 'adminguide/service_offerings.html#creating-a-new-compute-offering', + dataView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/offering/CloneComputeOffering.vue'))) }] }, { @@ -224,7 +232,16 @@ export default { } }, show: (record) => { return record.state === 'Active' }, - groupMap: (selection) => { return selection.map(x => { return { id: x } }) } + groupMap: (selection) => { return selection.map(x => { return { id: x, state: 'Inactive' } }) } + }, { + api: 'cloneServiceOffering', + icon: 'copy-outlined', + label: 'label.clone.system.service.offering', + docHelp: 'adminguide/service_offerings.html#creating-a-new-system-service-offering', + dataView: true, + params: { issystem: 'true' }, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/offering/CloneComputeOffering.vue'))) }] }, { @@ -331,7 +348,15 @@ export default { } }, show: (record) => { return record.state === 'Active' }, - groupMap: (selection) => { return selection.map(x => { return { id: x } }) } + groupMap: (selection) => { return selection.map(x => { return { id: x, state: 'Inactive' } }) } + }, { + api: 'cloneDiskOffering', + icon: 'copy-outlined', + label: 'label.clone.disk.offering', + docHelp: 'adminguide/service_offerings.html#creating-a-new-disk-offering', + dataView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/offering/CloneDiskOffering.vue'))) }] }, { @@ -340,9 +365,9 @@ export default { icon: 'cloud-upload-outlined', docHelp: 'adminguide/virtual_machines.html#backup-offerings', permission: ['listBackupOfferings'], - searchFilters: ['zoneid'], - columns: ['name', 'description', 'zonename'], - details: ['name', 'id', 'description', 'externalid', 'zone', 'allowuserdrivenbackups', 'created'], + searchFilters: ['zoneid', 'domainid'], + columns: ['name', 'description', 'domain', 'zonename'], + details: ['name', 'id', 'description', 'externalid', 'domain', 'zone', 'allowuserdrivenbackups', 'created', 'backupofferingdetails'], related: [{ name: 'vm', title: 'label.instances', @@ -368,6 +393,13 @@ export default { listView: true, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/offering/ImportBackupOffering.vue'))) + }, { + api: 'createBackupOffering', + icon: 'plus-outlined', + label: 'label.create.backup.offering', + listView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/offering/CreateBackupOffering.vue'))) }, { api: 'updateBackupOffering', icon: 'edit-outlined', @@ -376,6 +408,14 @@ export default { popup: true, groupMap: (selection) => { return selection.map(x => { return { id: x } }) }, args: ['name', 'description', 'allowuserdrivenbackups'] + }, { + api: 'cloneBackupOffering', + icon: 'copy-outlined', + label: 'label.clone.backup.offering', + docHelp: 'adminguide/virtual_machines.html#importing-backup-offerings', + dataView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/offering/CloneBackupOffering.vue'))) }, { api: 'deleteBackupOffering', icon: 'delete-outlined', @@ -487,6 +527,14 @@ export default { dataView: true, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/offering/UpdateOfferingAccess.vue'))) + }, { + api: 'cloneNetworkOffering', + icon: 'copy-outlined', + label: 'label.clone.network.offering', + docHelp: 'adminguide/networking.html#creating-a-new-network-offering', + dataView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/offering/CloneNetworkOffering.vue'))) }, { api: 'deleteNetworkOffering', icon: 'delete-outlined', @@ -508,7 +556,7 @@ export default { searchFilters: ['name', 'zoneid', 'domainid'], resourceType: 'VpcOffering', columns: ['name', 'state', 'displaytext', 'domain', 'zone', 'order'], - details: ['name', 'id', 'displaytext', 'internetprotocol', 'distributedvpcrouter', 'tags', 'routingmode', 'specifyasnumber', 'service', 'fornsx', 'networkmode', 'domain', 'zone', 'created'], + details: ['name', 'id', 'displaytext', 'internetprotocol', 'distributedvpcrouter', 'tags', 'routingmode', 'specifyasnumber', 'service', 'fornsx', 'networkmode', 'conservemode', 'domain', 'zone', 'created'], related: [{ name: 'vpc', title: 'label.vpc', @@ -579,6 +627,14 @@ export default { dataView: true, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/offering/UpdateOfferingAccess.vue'))) + }, { + api: 'cloneVPCOffering', + icon: 'copy-outlined', + docHelp: 'plugins/nuage-plugin.html?#optional-create-and-enable-vpc-offering', + label: 'label.clone.vpc.offering', + dataView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/offering/CloneVpcOffering.vue'))) }, { api: 'deleteVPCOffering', icon: 'delete-outlined', diff --git a/ui/src/config/section/plugin/quota.js b/ui/src/config/section/plugin/quota.js index 630e42e4c042..162fb74f7e75 100644 --- a/ui/src/config/section/plugin/quota.js +++ b/ui/src/config/section/plugin/quota.js @@ -30,29 +30,38 @@ export default { title: 'label.quota.summary', icon: 'bars-outlined', permission: ['quotaSummary'], - columns: ['account', + customParamHandler: (params, query) => { return { ...params, ignoreproject: true } }, + tabs: [ + { + name: 'consumption', + component: shallowRef(defineAsyncComponent(() => import('@/views/plugins/quota/QuotaUsageTab.vue'))) + }, { - state: (record) => record.state.toLowerCase() + name: 'balance', + component: shallowRef(defineAsyncComponent(() => import('@/views/plugins/quota/QuotaBalanceTab.vue'))) }, { - quotastate: (record) => record.quotaenabled ? 'Enabled' : 'Disabled' - }, 'domain', 'currency', 'balance' + name: 'credits', + component: shallowRef(defineAsyncComponent(() => import('@/views/plugins/quota/QuotaCreditTab.vue'))) + } ], - columnNames: ['account', 'accountstate', 'quotastate', 'domain', 'currency', 'currentbalance'], - details: ['account', 'domain', 'state', 'currency', 'balance', 'quota', 'startdate', 'enddate'], - component: shallowRef(() => import('@/views/plugins/quota/QuotaSummary.vue')), - tabs: [ + columns: [ + 'account', { - name: 'details', - component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) + field: 'state', + customTitle: 'accountState', + state: (record) => record.accountremoved || (record.projectid && record.projectremoved) ? 'disabled' : 'enabled' }, { - name: 'quota.statement.quota', - component: shallowRef(defineAsyncComponent(() => import('@/views/plugins/quota/QuotaUsage.vue'))) + field: 'quotastate', + customTitle: 'quotaState', + quotastate: (record) => !record.quotaenabled || record.accountremoved || (record.projectid && record.projectremoved) ? 'disabled' : 'enabled' }, + 'domain', + 'currency', { - name: 'quota.statement.balance', - component: shallowRef(defineAsyncComponent(() => import('@/views/plugins/quota/QuotaBalance.vue'))) + field: 'balance', + customTitle: 'quota.current.balance' } ], actions: [ @@ -61,16 +70,9 @@ export default { icon: 'plus-outlined', docHelp: 'plugins/quota.html#quota-credits', label: 'label.quota.add.credits', - dataView: true, - args: ['value', 'min_balance', 'quota_enforce'], - mapping: { - account: { - value: (record) => { return record.account } - }, - domainid: { - value: (record) => { return record.domainid } - } - } + listView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/plugins/quota/AddQuotaCredit.vue'))) } ] }, @@ -83,7 +85,7 @@ export default { customParamHandler: (params, query) => { params.listall = false - if (['all', 'removed'].includes(query.filter) || params.id) { + if (['all', 'removed'].includes(query.filter) || params.uuid) { params.listall = true } @@ -109,6 +111,11 @@ export default { field: 'tariffValue', customTitle: 'quota.tariff.value' }, + { + field: 'hasActivationRule', + customTitle: 'quota.tariff.hasactivationrule', + hasActivationRule: (record) => record.activationRule ? i18n.global.t('label.yes') : i18n.global.t('label.no') + }, { field: 'executionPosition', customTitle: 'quota.tariff.position', @@ -145,7 +152,11 @@ export default { field: 'endDate', customTitle: 'end.date' }, - 'removed' + 'removed', + { + field: 'activationRule', + customTitle: 'quota.tariff.activationrule' + } ], filters: ['all', 'active', 'removed'], searchFilters: ['usagetype'], @@ -173,13 +184,16 @@ export default { label: 'label.action.quota.tariff.remove', message: 'message.action.quota.tariff.remove', dataView: true, - show: (record) => !record.removed + show: (record) => !record.removed, + groupAction: true, + popup: true, + groupMap: (selection) => { return selection.map(x => { return { id: x } }) } } ] }, { name: 'quotaemailtemplate', - title: 'label.templatetype', + title: 'label.emailtemplate', icon: 'mail-outlined', permission: ['quotaEmailTemplateList'], columns: ['templatetype', 'templatesubject', 'templatebody'], diff --git a/ui/src/config/section/project.js b/ui/src/config/section/project.js index 5a1f5f71c81d..c08cb8dce4dc 100644 --- a/ui/src/config/section/project.js +++ b/ui/src/config/section/project.js @@ -162,6 +162,7 @@ export default { }, groupAction: true, popup: true, + requireNameConfirmation: true, groupMap: (selection, values) => { return selection.map(x => { return { id: x, cleanup: values.cleanup || null } }) }, args: (record, store) => { const fields = [] diff --git a/ui/src/config/section/storage.js b/ui/src/config/section/storage.js index 4308832bcf20..702b782690af 100644 --- a/ui/src/config/section/storage.js +++ b/ui/src/config/section/storage.js @@ -18,6 +18,7 @@ import { shallowRef, defineAsyncComponent } from 'vue' import store from '@/store' import { isZoneCreated } from '@/utils/zone' +import { isAdmin } from '@/role' export default { name: 'storage', @@ -63,7 +64,7 @@ export default { return fields }, - details: ['name', 'id', 'type', 'storagetype', 'diskofferingdisplaytext', 'deviceid', 'sizegb', 'physicalsize', 'provisioningtype', 'utilization', 'diskkbsread', 'diskkbswrite', 'diskioread', 'diskiowrite', 'diskiopstotal', 'miniops', 'maxiops', 'path', 'deleteprotection'], + details: ['name', 'id', 'type', 'storagetype', 'diskofferingdisplaytext', 'kmskey', 'deviceid', 'sizegb', 'physicalsize', 'provisioningtype', 'utilization', 'diskkbsread', 'diskkbswrite', 'diskioread', 'diskiowrite', 'diskiopstotal', 'miniops', 'maxiops', 'path', 'deleteprotection'], related: [{ name: 'snapshot', title: 'label.snapshots', @@ -92,7 +93,7 @@ export default { } ], searchFilters: () => { - var filters = ['name', 'zoneid', 'domainid', 'account', 'state', 'tags', 'serviceofferingid', 'diskofferingid', 'isencrypted'] + const filters = ['name', 'zoneid', 'domainid', 'account', 'state', 'tags', 'serviceofferingid', 'diskofferingid', 'kmskeyid', 'isencrypted'] if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { filters.push('storageid') } @@ -221,6 +222,25 @@ export default { popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/storage/MigrateVolume.vue'))) }, + { + api: 'migrateVolumesToKMS', + icon: 'lock-outlined', + docHelp: 'adminguide/kms.html#migrating-existing-volumes-to-kms', + label: 'label.migrate.volume.to.kms', + message: 'message.action.migrate.volume.to.kms', + dataView: true, + popup: true, + show: (record, store) => { + return record.encryptformat && !record.kmskeyid && + ['Ready', 'Allocated'].includes(record.state) + }, + args: ['kmskeyid'], + mapping: { + volumeids: { + value: (record) => { return record.id } + } + } + }, { api: 'changeOfferingForVolume', icon: 'swap-outlined', @@ -311,7 +331,10 @@ export default { permission: ['listSnapshots'], resourceType: 'Snapshot', columns: () => { - var fields = ['name', 'state', 'volumename', 'intervaltype', 'physicalsize', 'created'] + const fields = ['name', 'state', 'volumename', 'intervaltype', 'physicalsize', 'created'] + if (store.getters.features.snapshotshowchainsize) { + fields.splice(fields.indexOf('created'), 0, 'chainsize', 'parentname') + } if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { fields.push('account') if (store.getters.listAllProjects) { @@ -324,7 +347,13 @@ export default { fields.push('zonename') return fields }, - details: ['name', 'id', 'volumename', 'volumetype', 'snapshottype', 'intervaltype', 'physicalsize', 'virtualsize', 'chainsize', 'account', 'domain', 'created'], + details: () => { + const fields = ['name', 'id', 'volumename', 'volumetype', 'snapshottype', 'intervaltype', 'physicalsize', 'virtualsize', 'account', 'domain', 'created'] + if (store.getters.features.snapshotshowchainsize) { + fields.splice(fields.indexOf('account'), 0, 'chainsize', 'parentname') + } + return fields + }, tabs: [ { name: 'details', @@ -346,7 +375,7 @@ export default { } ], searchFilters: () => { - var filters = ['name', 'domainid', 'account', 'tags', 'zoneid'] + const filters = ['name', 'domainid', 'account', 'tags', 'zoneid'] if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { filters.push('storageid') filters.push('imagestoreid') @@ -413,16 +442,61 @@ export default { } ] }, + { + name: 'snapshotpolicy', + title: 'label.snapshotpolicies', + icon: 'build-outlined', + docHelp: 'adminguide/storage.html#working-with-volume-snapshots', + permission: ['listSnapshotPolicies'], + resourceType: 'SnapshotPolicy', + params: { listall: true }, + columns: () => { + var fields = ['intervaltype', 'maxsnaps', 'schedule', 'timezone', 'volumename'] + return fields + }, + searchFilters: ['volumeid'], + actions: [ + { + api: 'createSnapshotPolicy', + icon: 'plus-outlined', + docHelp: 'adminguide/storage.html#working-with-volume-snapshots', + label: 'label.action.create.recurring.snapshot', + listView: true, + show: () => { return 'createSnapshotPolicy' in store.getters.apis }, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/storage/RecurringSnapshotVolume.vue'))), + mapping: { + intervaltype: { + options: ['HOURLY', 'DAILY', 'WEEKLY', 'MONTHLY'] + } + } + }, + { + api: 'deleteSnapshotPolicies', + icon: 'delete-outlined', + label: 'label.delete.snapshot.policy', + message: 'message.action.delete.snapshot.policy', + dataView: true, + show: (record) => true, + args: ['id'], + mapping: { + id: { + value: (record) => record.id + } + } + } + ] + }, { name: 'backup', - title: 'label.backup', + title: 'label.backups', icon: 'cloud-upload-outlined', permission: ['listBackups'], params: { listvmdetails: 'true' }, - columns: ['name', 'status', 'size', 'virtualsize', 'virtualmachinename', 'backupofferingname', 'intervaltype', 'type', 'created', 'account', 'domain', 'zone'], + columns: ['name', 'status', 'compressionstatus', 'validationstatus', 'size', 'virtualsize', 'virtualmachinename', 'backupofferingname', 'intervaltype', 'type', 'created', 'account', 'domain', 'zone'], details: ['name', 'description', 'virtualmachinename', 'id', 'intervaltype', 'type', 'externalid', 'size', 'virtualsize', 'volumes', 'backupofferingname', 'zone', 'account', 'domain', 'created'], searchFilters: () => { - var filters = ['name', 'zoneid', 'domainid', 'account', 'backupofferingid'] + var filters = ['name', 'zoneid', 'domainid', 'account', 'backupofferingid', 'status'] return filters }, tabs: [ @@ -443,7 +517,14 @@ export default { label: 'label.backup.restore', message: 'message.backup.restore', dataView: true, - show: (record) => { return record.status === 'BackedUp' } + show: (record) => { return record.status === 'BackedUp' }, + args: () => { + const fields = ['quickrestore'] + if (isAdmin()) { + fields.push('hostid') + } + return fields + } }, { api: 'restoreVolumeFromBackupAndAttachToVM', @@ -497,6 +578,51 @@ export default { } ] }, + { + name: 'backupschedule', + title: 'label.backup.schedules', + icon: 'build-outlined', + docHelp: 'adminguide/storage.html#working-with-volume-snapshots', + permission: ['listBackupSchedule'], + resourceType: 'backupSchedule', + params: { listall: true }, + columns: () => { + var fields = ['intervaltype', 'maxbackups', 'schedule', 'timezone', 'virtualmachinename'] + return fields + }, + searchFilters: ['virtualmachineid'], + actions: [ + { + api: 'createBackupSchedule', + icon: 'plus-outlined', + docHelp: 'adminguide/storage.html#working-with-volume-snapshots', + label: 'label.action.create.backup.schedule', + listView: true, + show: () => { return 'createBackupSchedule' in store.getters.apis }, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/compute/backup/CreateBackupSchedule.vue'))), + mapping: { + intervaltype: { + options: ['HOURLY', 'DAILY', 'WEEKLY', 'MONTHLY'] + } + } + }, + { + api: 'deleteBackupSchedule', + icon: 'delete-outlined', + label: 'label.delete.backup.schedule', + message: 'message.action.delete.backup.schedule', + dataView: true, + show: (record) => true, + args: ['id'], + mapping: { + id: { + value: (record) => record.id + } + } + } + ] + }, { name: 'buckets', title: 'label.buckets', diff --git a/ui/src/config/section/tools.js b/ui/src/config/section/tools.js index a07228ca87b4..5b7f4b9af325 100644 --- a/ui/src/config/section/tools.js +++ b/ui/src/config/section/tools.js @@ -116,6 +116,10 @@ export default { name: 'details', component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) }, + { + name: 'filters', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/WebhookFiltersTab.vue'))) + }, { name: 'recent.deliveries', component: shallowRef(defineAsyncComponent(() => import('@/components/view/WebhookDeliveriesTab.vue'))) diff --git a/ui/src/config/section/user.js b/ui/src/config/section/user.js index a18994fd6ce1..a2808356233e 100644 --- a/ui/src/config/section/user.js +++ b/ui/src/config/section/user.js @@ -63,6 +63,12 @@ export default { resourceType: 'User', component: shallowRef(defineAsyncComponent(() => import('@/components/view/EventsTab.vue'))), show: () => { return 'listEvents' in store.getters.apis } + }, + { + name: 'apikeypairs', + resourceType: 'User', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/ApiKeyPairsTab.vue'))), + show: () => { return 'listUserKeys' in store.getters.apis } } ], actions: [ @@ -82,12 +88,34 @@ export default { popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/iam/EditUser.vue'))) }, + { + api: 'updateUser', + icon: 'redo-outlined', + label: 'label.change.password.reset', + message: 'message.change.password.reset', + dataView: true, + args: ['passwordchangerequired'], + mapping: { + passwordchangerequired: { + value: (record) => { return true } + } + }, + popup: true, + show: (record, store) => { + return ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) && !record.isdefault && + store.userInfo.id !== record.id && record.state === 'enabled' && record.usersource === 'native' + } + }, { api: 'updateUser', icon: 'key-outlined', label: 'label.action.change.password', dataView: true, popup: true, + show: (record, store) => { + return (['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) || store.userInfo.id === record.id) && + ['native'].includes(record.usersource) && record.state === 'enabled' + }, component: shallowRef(defineAsyncComponent(() => import('@/views/iam/ChangeUserPassword.vue'))) }, { @@ -105,9 +133,10 @@ export default { message: 'message.enable.user', dataView: true, show: (record, store) => { - return ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) && !record.isdefault && - !(record.domain === 'ROOT' && record.account === 'admin' && record.accounttype === 1) && - ['disabled', 'locked'].includes(record.state) + if (!['disabled', 'locked'].includes(record.state) || record.isdefault || !['Admin', 'DomainAdmin'].includes(store.userInfo.roletype)) { + return false + } + return ![1, 4].includes(record.accounttype) || store.userInfo.roletype === 'Admin' } }, { @@ -117,9 +146,10 @@ export default { message: 'message.disable.user', dataView: true, show: (record, store) => { - return ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) && !record.isdefault && - !(record.domain === 'ROOT' && record.account === 'admin' && record.accounttype === 1) && - record.state === 'enabled' + if (record.state !== 'enabled' || record.isdefault || !['Admin', 'DomainAdmin'].includes(store.userInfo.roletype)) { + return false + } + return ![1, 4].includes(record.accounttype) || (store.userInfo.roletype === 'Admin' && record.id !== store.userInfo.id) } }, { @@ -131,9 +161,10 @@ export default { dataView: true, popup: true, show: (record, store) => { - return ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) && !record.isdefault && - !(record.domain === 'ROOT' && record.account === 'admin' && record.accounttype === 1) && - record.state === 'enabled' + if (record.state !== 'enabled' || record.isdefault || !['Admin', 'DomainAdmin'].includes(store.userInfo.roletype)) { + return false + } + return ![1, 4].includes(record.accounttype) || (store.userInfo.roletype === 'Admin' && record.id !== store.userInfo.id) } }, { @@ -154,7 +185,7 @@ export default { dataView: true, popup: true, show: (record, store) => { - return (record.is2faenabled === false && record.id === store.userInfo.id) + return (!record.is2faenabled && record.id === store.userInfo.id) }, component: shallowRef(defineAsyncComponent(() => import('@/views/iam/SetupTwoFaAtUserProfile.vue'))) }, diff --git a/ui/src/core/lazy_lib/icons_use.js b/ui/src/core/lazy_lib/icons_use.js index 502eb5de0b63..43c6f822de31 100644 --- a/ui/src/core/lazy_lib/icons_use.js +++ b/ui/src/core/lazy_lib/icons_use.js @@ -183,6 +183,7 @@ import { UploadOutlined, VerticalAlignBottomOutlined, VerticalAlignTopOutlined, + VerticalAlignMiddleOutlined, WarningOutlined, WifiOutlined, SolutionOutlined @@ -358,6 +359,7 @@ export default { app.component('UploadOutlined', UploadOutlined) app.component('VerticalAlignBottomOutlined', VerticalAlignBottomOutlined) app.component('VerticalAlignTopOutlined', VerticalAlignTopOutlined) + app.component('VerticalAlignMiddleOutlined', VerticalAlignMiddleOutlined) app.component('WarningOutlined', WarningOutlined) app.component('WifiOutlined', WifiOutlined) app.component('renderIcon', renderIcon) diff --git a/ui/src/locales/index.js b/ui/src/locales/index.js index 6933e05206e1..929ed410a6d5 100644 --- a/ui/src/locales/index.js +++ b/ui/src/locales/index.js @@ -18,46 +18,54 @@ import { createI18n } from 'vue-i18n' import { vueProps } from '@/vue-app' +const FALLBACK_LANG = 'en' const loadedLanguage = [] const messages = {} export const i18n = createI18n({ - locale: 'en', - fallbackLocale: 'en', + locale: FALLBACK_LANG, + fallbackLocale: FALLBACK_LANG, silentTranslationWarn: true, messages: messages, silentFallbackWarn: true, warnHtmlInMessage: 'off' }) -export function loadLanguageAsync (lang) { - if (!lang) { - const locale = vueProps.$localStorage.get('LOCALE') - lang = (!locale || typeof locale === 'object') ? 'en' : locale +function applyMessages (lang, message) { + if (message && Object.keys(message).length > 0) { + i18n.global.setLocaleMessage(lang, message) + messages[lang] = message } - if (loadedLanguage.includes(lang)) { - return Promise.resolve(setLanguage(lang)) + if (!loadedLanguage.includes(lang)) { + loadedLanguage.push(lang) } +} +function fetchLocale (lang) { return fetch(`locales/${lang}.json?ts=${Date.now()}`) .then(response => response.json()) - .then(json => Promise.resolve(setLanguage(lang, json))) + .then(json => applyMessages(lang, json)) } -function setLanguage (lang, message) { - if (i18n) { - i18n.global.locale = lang - - if (message && Object.keys(message).length > 0) { - i18n.global.setLocaleMessage(lang, message) - } +export function loadLanguageAsync (lang) { + if (!lang) { + const locale = vueProps.$localStorage.get('LOCALE') + lang = (!locale || typeof locale === 'object') ? FALLBACK_LANG : locale } - if (!loadedLanguage.includes(lang)) { - loadedLanguage.push(lang) - } + // Always keep the fallback locale's messages loaded so $t() degrades + // to readable English instead of raw keys when a translation is missing. + const ensureFallback = loadedLanguage.includes(FALLBACK_LANG) + ? Promise.resolve() + : fetchLocale(FALLBACK_LANG) - if (message && Object.keys(message).length > 0) { - messages[lang] = message - } + const ensureTarget = (lang === FALLBACK_LANG || loadedLanguage.includes(lang)) + ? ensureFallback + : ensureFallback.then(() => fetchLocale(lang)) + + // Activate locale after messages are in place so the first render + // already has the translations and avoids a flash of raw keys. + return ensureTarget.then(() => { + i18n.global.locale = lang + }) } diff --git a/ui/src/main.js b/ui/src/main.js index f117fb578108..7441f8010865 100644 --- a/ui/src/main.js +++ b/ui/src/main.js @@ -40,7 +40,8 @@ import { dialogUtilPlugin, cpuArchitectureUtilPlugin, imagesUtilPlugin, - extensionsUtilPlugin + extensionsUtilPlugin, + backupUtilPlugin } from './utils/plugins' import { VueAxios } from './utils/request' import directives from './utils/directives' @@ -63,6 +64,7 @@ vueApp.use(dialogUtilPlugin) vueApp.use(cpuArchitectureUtilPlugin) vueApp.use(imagesUtilPlugin) vueApp.use(extensionsUtilPlugin) +vueApp.use(backupUtilPlugin) vueApp.use(extensions) vueApp.use(directives) diff --git a/ui/src/permission.js b/ui/src/permission.js index 266dc992c8db..b0ed66365e68 100644 --- a/ui/src/permission.js +++ b/ui/src/permission.js @@ -93,6 +93,20 @@ router.beforeEach((to, from, next) => { return } store.commit('SET_LOGIN_FLAG', true) + const MS_ID = Cookies.get('managementserverid') + if (MS_ID) { + store.commit('SET_MS_ID', MS_ID) + } + } + // store already loaded + if (store.getters.passwordChangeRequired) { + if (to.path === '/user/forceChangePassword') { + next() + } else { + next({ path: '/user/forceChangePassword' }) + NProgress.done() + } + return } if (Object.keys(store.getters.apis).length === 0) { const cachedApis = vueProps.$localStorage.get(APIS, {}) @@ -102,6 +116,19 @@ router.beforeEach((to, from, next) => { store .dispatch('GetInfo') .then(apis => { + // Essential for Page Refresh scenarios + if (store.getters.passwordChangeRequired) { + // Only allow the Change Password page + if (to.path === '/user/forceChangePassword') { + next() + } else { + // Redirect everything else (including dashboard, wildcards) to Change Password + next({ path: '/user/forceChangePassword' }) + NProgress.done() + } + return + } + store.dispatch('GenerateRoutes', { apis }).then(() => { store.getters.addRouters.map(route => { router.addRoute(route) @@ -146,7 +173,7 @@ router.beforeEach((to, from, next) => { } } } else { - if (window.location.href.includes('verifyOauth') && to.name === undefined) { + if (window.location.search.includes('verifyOauth') && to.name !== 'VerifyOauth') { currentURL = new URL(window.location.href) urlParams = new URLSearchParams(currentURL.search) code = urlParams.get('code') diff --git a/ui/src/store/getters.js b/ui/src/store/getters.js index 911234d9b715..c7ab2f0c536b 100644 --- a/ui/src/store/getters.js +++ b/ui/src/store/getters.js @@ -55,7 +55,8 @@ const getters = { loginFlag: state => state.user.loginFlag, allProjects: (state) => state.app.allProjects, customHypervisorName: state => state.user.customHypervisorName, - readyForShutdownPollingJob: state => state.user.readyForShutdownPollingJob + readyForShutdownPollingJob: state => state.user.readyForShutdownPollingJob, + passwordChangeRequired: state => state.user.passwordChangeRequired } export default getters diff --git a/ui/src/store/modules/user.js b/ui/src/store/modules/user.js index 51037164aed4..a18626e801bc 100644 --- a/ui/src/store/modules/user.js +++ b/ui/src/store/modules/user.js @@ -44,7 +44,8 @@ import { MS_ID, OAUTH_DOMAIN, OAUTH_PROVIDER, - LATEST_CS_VERSION + LATEST_CS_VERSION, + PASSWORD_CHANGE_REQUIRED } from '@/store/mutation-types' import { @@ -80,7 +81,8 @@ const user = { twoFaProvider: '', twoFaIssuer: '', customHypervisorName: 'Custom', - readyForShutdownPollingJob: '' + readyForShutdownPollingJob: '', + passwordChangeRequired: false }, mutations: { @@ -196,6 +198,14 @@ const user = { vueProps.$localStorage.set(LATEST_CS_VERSION, version) state.latestVersion = version } + }, + SET_PASSWORD_CHANGE_REQUIRED: (state, required) => { + state.passwordChangeRequired = required + if (required) { + vueProps.$localStorage.set(PASSWORD_CHANGE_REQUIRED, true) + } else { + vueProps.$localStorage.remove(PASSWORD_CHANGE_REQUIRED) + } } }, @@ -244,6 +254,13 @@ const user = { if (result && result.managementserverid) { commit('SET_MS_ID', result.managementserverid) } + if (result.passwordchangerequired) { + commit('SET_PASSWORD_CHANGE_REQUIRED', true) + commit('SET_APIS', {}) + vueProps.$localStorage.remove(APIS) + } else { + commit('SET_PASSWORD_CHANGE_REQUIRED', false) + } const latestVersion = vueProps.$localStorage.get(LATEST_CS_VERSION, { version: '', fetchedTs: 0 }) commit('SET_LATEST_VERSION', latestVersion) notification.destroy() @@ -323,6 +340,49 @@ const user = { commit('SET_DOMAIN_STORE', domainStore) commit('SET_DARK_MODE', darkMode) commit('SET_LATEST_VERSION', latestVersion) + + const loadFeatures = (apis) => { + return new Promise(resolve => { + getAPI('listCapabilities').then(response => { + const result = response.listcapabilitiesresponse.capability + commit('SET_FEATURES', result) + if (result && result.defaultuipagesize) { + commit('SET_DEFAULT_LISTVIEW_PAGE_SIZE', result.defaultuipagesize) + } + if (result && result.customhypervisordisplayname) { + commit('SET_CUSTOM_HYPERVISOR_NAME', result.customhypervisordisplayname) + } + if (result && result.securitygroupsenabled) { + commit('SET_SHOW_SECURITY_GROUPS', result.securitygroupsenabled) + } + + if ('listHSMProfiles' in apis) { + getAPI('listHSMProfiles', { listall: true }).then(response => { + const hasHsmProfiles = (response.listhsmprofilesresponse.count > 0) + const features = Object.assign({}, store.getters.features) + features.hashsmprofiles = hasHsmProfiles + commit('SET_FEATURES', features) + resolve() + }).catch(ignored => { + resolve() + }) + } else { + resolve() + } + }).catch(() => { + resolve() + }) + }) + } + + // This block is to enforce password change for first time login after admin resets password + const isPwdChangeRequired = vueProps.$localStorage.get(PASSWORD_CHANGE_REQUIRED) + commit('SET_PASSWORD_CHANGE_REQUIRED', isPwdChangeRequired) + if (isPwdChangeRequired) { + resolve() + return + } + if (hasAuth) { console.log('Login detected, using cached APIs') commit('SET_ZONES', cachedZones) @@ -338,8 +398,9 @@ const user = { const result = response.listusersresponse.user[0] commit('SET_INFO', result) commit('SET_NAME', result.firstname + ' ' + result.lastname) - store.dispatch('SetCsLatestVersion', result.rolename) - resolve(cachedApis) + loadFeatures(cachedApis).then(() => { + resolve(cachedApis) + }) }).catch(error => { reject(error) }) @@ -366,14 +427,16 @@ const user = { } } commit('SET_APIS', apis) - resolve(apis) - store.dispatch('GenerateRoutes', { apis }).then(() => { - store.getters.addRouters.map(route => { - router.addRoute(route) + loadFeatures(apis).then(() => { + resolve(apis) + store.dispatch('GenerateRoutes', { apis }).then(() => { + store.getters.addRouters.map(route => { + router.addRoute(route) + }) }) + hide() + message.success(i18n.global.t('message.sussess.discovering.feature')) }) - hide() - message.success(i18n.global.t('message.sussess.discovering.feature')) }).catch(error => { reject(error) }) @@ -427,22 +490,6 @@ const user = { }).catch(ignored => { }) - getAPI('listCapabilities').then(response => { - const result = response.listcapabilitiesresponse.capability - commit('SET_FEATURES', result) - if (result && result.defaultuipagesize) { - commit('SET_DEFAULT_LISTVIEW_PAGE_SIZE', result.defaultuipagesize) - } - if (result && result.customhypervisordisplayname) { - commit('SET_CUSTOM_HYPERVISOR_NAME', result.customhypervisordisplayname) - } - if (result && result.securitygroupsenabled) { - commit('SET_SHOW_SECURITY_GROUPS', result.securitygroupsenabled) - } - }).catch(error => { - reject(error) - }) - getAPI('listLdapConfigurations').then(response => { const ldapEnable = (response.ldapconfigurationresponse.count > 0) commit('SET_LDAP', ldapEnable) @@ -486,6 +533,8 @@ const user = { vueProps.$localStorage.remove(ACCESS_TOKEN) vueProps.$localStorage.remove(HEADER_NOTICES) + commit('SET_PASSWORD_CHANGE_REQUIRED', false) + logout(state.token).then(() => { message.destroy() if (cloudianUrl) { @@ -559,19 +608,21 @@ const user = { getAPI('listCapabilities').then(response => { const result = response.listcapabilitiesresponse.capability resolve(result) - commit('SET_FEATURES', result) - }).catch(error => { - reject(error) - }) - - getAPI('listConfigurations', { name: 'hypervisor.custom.display.name' }).then(json => { - if (json.listconfigurationsresponse.configuration !== null) { - const config = json.listconfigurationsresponse.configuration[0] - commit('SET_CUSTOM_HYPERVISOR_NAME', config.value) - } + const features = Object.assign({}, store.getters.features, result) + commit('SET_FEATURES', features) }).catch(error => { reject(error) }) + if ('listConfigurations' in store.getters.apis) { + getAPI('listConfigurations', { name: 'hypervisor.custom.display.name' }).then(json => { + if (json.listconfigurationsresponse.configuration !== null) { + const config = json.listconfigurationsresponse.configuration[0] + commit('SET_CUSTOM_HYPERVISOR_NAME', config.value) + } + }).catch(error => { + reject(error) + }) + } }) }, UpdateConfiguration ({ commit }) { @@ -588,6 +639,9 @@ const user = { commit('SET_DOMAIN_STORE', domainStore) }, SetCsLatestVersion ({ commit }, rolename) { + if (!vueProps.$config.notifyLatestCSVersion) { + return + } const lastFetchTs = store.getters.latestVersion?.fetchedTs ? store.getters.latestVersion.fetchedTs : 0 if (rolename === 'Root Admin' && (+new Date() - lastFetchTs) > 24 * 60 * 60 * 1000) { axios.get( diff --git a/ui/src/store/mutation-types.js b/ui/src/store/mutation-types.js index 0b1f921ab86e..5fc2cd74d213 100644 --- a/ui/src/store/mutation-types.js +++ b/ui/src/store/mutation-types.js @@ -43,6 +43,7 @@ export const RELOAD_ALL_PROJECTS = 'RELOAD_ALL_PROJECTS' export const MS_ID = 'MS_ID' export const OAUTH_DOMAIN = 'OAUTH_DOMAIN' export const OAUTH_PROVIDER = 'OAUTH_PROVIDER' +export const PASSWORD_CHANGE_REQUIRED = 'PASSWORD_CHANGE_REQUIRED' export const CONTENT_WIDTH_TYPE = { Fluid: 'Fluid', diff --git a/ui/src/style/README.md b/ui/src/style/README.md index 8e3fd832bd5c..ee54d6ed7cd2 100644 --- a/ui/src/style/README.md +++ b/ui/src/style/README.md @@ -1,3 +1,22 @@ + + # index.less - src/styles/index.less imports all necessary rules for cloudstack diff --git a/ui/src/style/ant-overwrite/ant-form.less b/ui/src/style/ant-overwrite/ant-form.less index 52a2086fb4ff..882724b8ecc3 100644 --- a/ui/src/style/ant-overwrite/ant-form.less +++ b/ui/src/style/ant-overwrite/ant-form.less @@ -23,6 +23,6 @@ transition: transform .3s, -webkit-transform .3s; } -.ant-select-open .ant-select-arrow .ant-select-suffix svg { +.ant-select-open .ant-select-arrow .ant-select-suffix:not(.anticon-search) svg { transform: rotateZ(-180deg); } diff --git a/ui/src/style/common/common.scss b/ui/src/style/common/common.scss new file mode 100644 index 000000000000..39dcb46457e7 --- /dev/null +++ b/ui/src/style/common/common.scss @@ -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. + +.w-100 { + width: 100%; +} + +.mt-10 { + margin-top: 10px; +} + +.mb-10 { + margin-bottom: 10px; +} + +.m-20-0 { + margin: 20px 0; +} + +.dotted-underline { + text-decoration: underline dotted; + cursor: default; +} diff --git a/ui/src/style/vars.less b/ui/src/style/vars.less index de2d494c878f..133244473e2e 100644 --- a/ui/src/style/vars.less +++ b/ui/src/style/vars.less @@ -355,7 +355,7 @@ a { text-align: right; padding-top: 15px; - button { + button, a.ant-btn { margin-right: 5px; } } diff --git a/ui/src/utils/acsrepo/index.js b/ui/src/utils/acsrepo/index.js new file mode 100644 index 000000000000..809bd7f17483 --- /dev/null +++ b/ui/src/utils/acsrepo/index.js @@ -0,0 +1,81 @@ +// 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. + +const BASE_KUBERNETES_ISO_URL = 'https://download.cloudstack.org/cks/' + +function getDefaultLatestKubernetesIsoParams (arch) { + return { + name: 'v1.33.1-calico-' + arch, + semanticversion: '1.33.1', + url: BASE_KUBERNETES_ISO_URL + 'setup-v1.33.1-calico-' + arch + '.iso', + arch: arch, + mincpunumber: 2, + minmemory: 2048 + } +} + +/** + * Returns the latest Kubernetes ISO info for the given architecture. + * Falls back to a hardcoded default if fetching fails. + * @param {string} arch + * @returns {Promise<{name: string, semanticversion: string, url: string, arch: string}>} + */ +export async function getLatestKubernetesIsoParams (arch) { + arch = arch || 'x86_64' + try { + const html = await fetch(BASE_KUBERNETES_ISO_URL, { cache: 'no-store' }).then(r => r.text()) + + const hrefs = [...html.matchAll(/href="([^"]+\.iso)"/gi)].map(m => m[1]) + + // Prefer files that explicitly include the arch (e.g. ...-x86_64.iso) + let isoHrefs = hrefs.filter(h => new RegExp(`${arch}\\.iso$`, 'i').test(h)) + + // Fallback: older files without arch suffix (e.g. setup-1.28.4.iso) + if (isoHrefs.length === 0) { + isoHrefs = hrefs.filter(h => /setup-\d+\.\d+\.\d+\.iso$/i.test(h)) + } + + const entries = isoHrefs.map(h => { + const m = h.match(/setup-(?:v)?(\d+\.\d+\.\d+)(?:-calico)?(?:-(x86_64|arm64))?/i) + return m + ? { + name: h.replace('.iso', ''), + semanticversion: m[1], + url: new URL(h, BASE_KUBERNETES_ISO_URL).toString(), + arch: m[2] || arch, + mincpunumber: 2, + minmemory: 2048 + } + : null + }).filter(Boolean) + + if (entries.length === 0) throw new Error('No matching ISOs found') + + entries.sort((a, b) => { + const pa = a.semanticversion.split('.').map(Number) + const pb = b.semanticversion.split('.').map(Number) + for (let i = 0; i < 3; i++) { + if ((pb[i] ?? 0) !== (pa[i] ?? 0)) return (pb[i] ?? 0) - (pa[i] ?? 0) + } + return 0 + }) + + return entries[0] + } catch { + return { ...getDefaultLatestKubernetesIsoParams(arch) } + } +} diff --git a/ui/src/utils/chart.js b/ui/src/utils/chart.js new file mode 100644 index 000000000000..a4e78d896163 --- /dev/null +++ b/ui/src/utils/chart.js @@ -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. + +import { TIME_UNITS } from './units' + +export const defaultDisplayFormats = { + day: 'DD MMM YYYY', + week: 'DD MMM YYYY', + month: 'MMM YYYY', + quarter: 'MMM YYYY', + year: 'YYYY' +} + +export const getUnitToTimeCartesianAxis = (baseUnit, dataLength) => { + const maxLabels = 15 + if (dataLength <= maxLabels) { + return baseUnit + } + + const units = [ + 'millisecond', + 'second', + 'minute', + 'hour', + 'day', + 'week', + 'month', + 'quarter', + 'year' + ] + + let index = units.indexOf(baseUnit) + + let unitToReturn = baseUnit + if (index >= 0 && index < units.length) { + let unitTime = 0 + for (index; index < units.length; index++) { + unitTime = TIME_UNITS[units[index]] + const nextUnitTime = TIME_UNITS[units[index + 1]] + + if ((dataLength / (nextUnitTime / unitTime)) <= maxLabels) { + return units[index + 1] + } + + unitToReturn = units[index] + } + } + + return unitToReturn +} + +export const getChartColorObject = (hexColor = '#1890FF') => ({ + backgroundColor: hexColor.concat('80'), + borderColor: hexColor, + borderWidth: 1.5 +}) diff --git a/ui/src/utils/date.js b/ui/src/utils/date.js index 216dfde1303b..bd2ea64e32fa 100644 --- a/ui/src/utils/date.js +++ b/ui/src/utils/date.js @@ -65,16 +65,21 @@ export function parseDateToDatePicker (value) { } export function toLocalDate ({ date, timezoneoffset = store.getters.timezoneoffset, usebrowsertimezone = store.getters.usebrowsertimezone }) { - if (usebrowsertimezone) { - // Since GMT+530 is returned as -330 (minutes to GMT) - timezoneoffset = new Date().getTimezoneOffset() / -60 - } + timezoneoffset = getTimezoneOffset({ timezoneoffset, usebrowsertimezone }) const milliseconds = Date.parse(date) // e.g. "Tue, 08 Jun 2010 19:13:49 GMT"; "Tue, 25 May 2010 12:07:01 UTC" return new Date(milliseconds + (timezoneoffset * 60 * 60 * 1000)) } +export function getTimezoneOffset ({ timezoneoffset = store.getters.timezoneoffset, usebrowsertimezone = store.getters.usebrowsertimezone }) { + if (!usebrowsertimezone) { + return timezoneoffset + } + // Since GMT+530 is returned as -330 (mins to GMT) + return new Date().getTimezoneOffset() / -60 +} + export function toLocaleDate ({ date, timezoneoffset = store.getters.timezoneoffset, usebrowsertimezone = store.getters.usebrowsertimezone, dateOnly = false, hourOnly = false }) { if (!date) { return null diff --git a/ui/src/utils/guiTheme.js b/ui/src/utils/guiTheme.js index b1a7209fd270..dab82287d111 100644 --- a/ui/src/utils/guiTheme.js +++ b/ui/src/utils/guiTheme.js @@ -17,6 +17,7 @@ import { vueProps } from '@/vue-app' import { getAPI } from '@/api' +import { loadLanguageAsync } from '../locales' export async function applyCustomGuiTheme (accountid, domainid) { await fetch('config.json').then(response => response.json()).then(config => { @@ -62,6 +63,11 @@ async function applyDynamicCustomization (response) { jsonConfig = JSON.parse(response?.jsonconfiguration) } + vueProps.$config.loginBaseDomain = '' + if (response?.loginbasedomain) { + vueProps.$config.loginBaseDomain = response.loginbasedomain + } + // Sets custom GUI fields only if is not nullish. vueProps.$config.appTitle = jsonConfig?.appTitle ?? vueProps.$config.appTitle vueProps.$config.footer = jsonConfig?.footer ?? vueProps.$config.footer @@ -69,12 +75,15 @@ async function applyDynamicCustomization (response) { vueProps.$config.logo = jsonConfig?.logo ?? vueProps.$config.logo vueProps.$config.minilogo = jsonConfig?.minilogo ?? vueProps.$config.minilogo vueProps.$config.banner = jsonConfig?.banner ?? vueProps.$config.banner + vueProps.$config.docBase = jsonConfig?.docBase ?? vueProps.$config.docBase + vueProps.$config.apidocs = jsonConfig?.apidocs ?? vueProps.$config.apidocs + vueProps.$config.docHelpMappings = jsonConfig?.docHelpMappings ?? vueProps.$config.docHelpMappings + vueProps.$config.keyboardOptions = jsonConfig?.keyboardOptions ?? vueProps.$config.keyboardOptions + vueProps.$config.defaultLanguage = vueProps.$localStorage.get('LOCALE') ?? jsonConfig?.defaultLanguage ?? vueProps.$config.defaultLanguage - if (jsonConfig?.error) { - vueProps.$config.error[403] = jsonConfig?.error[403] ?? vueProps.$config.error[403] - vueProps.$config.error[404] = jsonConfig?.error[404] ?? vueProps.$config.error[404] - vueProps.$config.error[500] = jsonConfig?.error[500] ?? vueProps.$config.error[500] - } + applyJsonConfigToObject(jsonConfig?.error, vueProps.$config.error) + applyJsonConfigToObject(jsonConfig?.userCard, vueProps.$config.userCard) + applyJsonConfigToObject(jsonConfig?.theme, vueProps.$config.theme) if (jsonConfig?.plugins) { jsonConfig.plugins.forEach(plugin => { @@ -82,12 +91,32 @@ async function applyDynamicCustomization (response) { }) } + if (vueProps.$store) { + vueProps.$store.dispatch('SetDarkMode', (vueProps.$config.theme['@layout-mode'] === 'dark')) + } + window.less.modifyVars(vueProps.$config.theme) + vueProps.$config.favicon = jsonConfig?.favicon ?? vueProps.$config.favicon vueProps.$config.css = response?.css ?? null + if (vueProps.$config.defaultLanguage) { + vueProps.$localStorage.set('LOCALE', vueProps.$config.defaultLanguage) + loadLanguageAsync() + } + await applyStaticCustomization(vueProps.$config.favicon, vueProps.$config.css) } +function applyJsonConfigToObject (sourceConfig, targetObject) { + if (!sourceConfig) { + return + } + + for (const [variableName, value] of Object.entries(targetObject)) { + targetObject[variableName] = sourceConfig?.[variableName] ?? value + } +} + async function applyStaticCustomization (favicon, css) { document.getElementById('favicon').href = favicon diff --git a/ui/src/utils/links.js b/ui/src/utils/links.js index fa650cdd7b8b..9beafc4a9869 100644 --- a/ui/src/utils/links.js +++ b/ui/src/utils/links.js @@ -15,6 +15,27 @@ // specific language governing permissions and limitations // under the License. +import { getAPI } from '@/api' + +async function isValidObject (apiName, id, params) { + try { + const allParams = { ...params, listAll: true, id } + const json = await getAPI(apiName, allParams) + const responseName = Object.keys(json).find(key => key.endsWith('response')) || apiName.toLowerCase() + 'response' + const response = json?.[responseName] + if (!response) { + return false + } + const objectName = Object.keys(response).find(key => key !== 'count') + if (!objectName || !Array.isArray(response[objectName])) { + return false + } + return response[objectName].some(item => item.id === id) + } catch (e) { + return false + } +} + export function validateLinks (router, isStatic, resource) { const validLinks = { volume: false @@ -34,3 +55,54 @@ export function validateLinks (router, isStatic, resource) { return validLinks } + +export async function validateLinksAsync (router, isStatic, resource) { + const validLinks = { + volume: false, + template: false, + iso: false + } + const pendingChecks = [] + + if (isStatic) { + return validLinks + } + + if (resource.volumeid && router.resolve('/volume/' + resource.volumeid).matched[0].redirect !== '/exception/404') { + if (resource.volumestate) { + validLinks.volume = resource.volumestate !== 'Expunged' + } else { + validLinks.volume = true + } + } + + if (resource.templateid) { + const templatePath = (resource.templateformat === 'ISO' ? '/iso/' : '/template/') + resource.templateid + if (router.resolve(templatePath).matched[0].redirect !== '/exception/404') { + pendingChecks.push( + isValidObject('listTemplates', resource.templateid, { templatefilter: 'executable' }).then(result => { + validLinks.template = result + }) + ) + } + } + + if (resource.isoid) { + const isoPath = '/iso/' + resource.isoid + if (router.resolve(isoPath).matched[0].redirect !== '/exception/404') { + pendingChecks.push( + isValidObject('listIsos', resource.isoid, { isofilter: 'executable' }).then(result => { + validLinks.iso = result + }) + ) + } + } + + if (pendingChecks.length) { + await Promise.all(pendingChecks).catch(error => { + console.error('Error validating links:', error) + }) + } + + return validLinks +} diff --git a/ui/src/utils/plugins.js b/ui/src/utils/plugins.js index 61456d98b124..b8856c7c9b00 100644 --- a/ui/src/utils/plugins.js +++ b/ui/src/utils/plugins.js @@ -224,18 +224,19 @@ export const notifierPlugin = { if (error.response.status) { msg = `${i18n.global.t('message.request.failed')} (${error.response.status})` } - if (error.message) { - desc = error.message - } - if (error.response.headers && 'x-description' in error.response.headers) { + if (error.response.headers?.['x-description']) { desc = error.response.headers['x-description'] - } - if (desc === '' && error.response.data) { + } else if (error.response.data) { const responseKey = _.findKey(error.response.data, 'errortext') if (responseKey) { desc = error.response.data[responseKey].errortext + } else if (typeof error.response.data === 'string') { + desc = error.response.data } } + if (!desc && error.message) { + desc = error.message + } } let countNotify = store.getters.countNotify countNotify++ @@ -549,6 +550,17 @@ export const dialogUtilPlugin = { onOk: () => callback(configRecord) }) } + + app.config.globalProperties.$notifyConfigurationValueChange = function (configRecord) { + if (!configRecord || configRecord.isdynamic || store.getters.userInfo?.roletype !== 'Admin') { + return + } + const server = configRecord.group === 'Usage Server' ? 'usage' : 'mgmt' + this.$notification.warning({ + message: this.$t('label.status'), + description: this.$t('message.restart.' + server + '.server') + }) + } } } @@ -557,7 +569,8 @@ export const cpuArchitectureUtilPlugin = { app.config.globalProperties.$fetchCpuArchitectureTypes = function () { const architectures = [ { id: 'x86_64', name: 'Intel/AMD 64 bits (x86_64)' }, - { id: 'aarch64', name: 'ARM 64 bits (aarch64)' } + { id: 'aarch64', name: 'ARM 64 bits (aarch64)' }, + { id: 's390x', name: 'IBM Z 64 bits (s390x)' } ] return architectures.map(item => ({ ...item, description: item.name })) } @@ -597,3 +610,14 @@ export const extensionsUtilPlugin = { } } } + +export const backupUtilPlugin = { + install (app) { + app.config.globalProperties.$isBackupProviderSupportsQuiesceVm = function (provider) { + if (!provider && typeof provider !== 'string') { + return false + } + return ['nas'].includes(provider.toLowerCase()) + } + } +} diff --git a/ui/src/utils/quota.js b/ui/src/utils/quota.js index b8adbb93518a..c23deb8d13b1 100644 --- a/ui/src/utils/quota.js +++ b/ui/src/utils/quota.js @@ -19,106 +19,135 @@ export const QUOTA_TYPES = [ { id: 1, - type: 'RUNNING_VM' + type: 'RUNNING_VM', + chartColor: '#1890ff' }, { id: 2, - type: 'ALLOCATED_VM' + type: 'ALLOCATED_VM', + chartColor: '#fadb14' }, { id: 3, - type: 'IP_ADDRESS' + type: 'IP_ADDRESS', + chartColor: '#ffd6e7' }, { id: 4, - type: 'NETWORK_BYTES_SENT' + type: 'NETWORK_BYTES_SENT', + chartColor: '#adc6ff' }, { id: 5, - type: 'NETWORK_BYTES_RECEIVED' + type: 'NETWORK_BYTES_RECEIVED', + chartColor: '#10239e' }, { id: 6, - type: 'VOLUME' + type: 'VOLUME', + chartColor: '#722ed1' }, { id: 7, - type: 'TEMPLATE' + type: 'TEMPLATE', + chartColor: '#08979c' }, { id: 8, - type: 'ISO' + type: 'ISO', + chartColor: '#87e8de' }, { id: 9, - type: 'SNAPSHOT' + type: 'SNAPSHOT', + chartColor: '#f5222d' }, { id: 10, - type: 'SECURITY_GROUP' + type: 'SECURITY_GROUP', + chartColor: '#d46b08' }, { id: 11, - type: 'LOAD_BALANCER_POLICY' + type: 'LOAD_BALANCER_POLICY', + chartColor: '#ffd666' }, { id: 12, - type: 'PORT_FORWARDING_RULE' + type: 'PORT_FORWARDING_RULE', + chartColor: '#7cb305' }, { id: 13, - type: 'NETWORK_OFFERING' + type: 'NETWORK_OFFERING', + chartColor: '#ffbb96' }, { id: 14, - type: 'VPN_USERS' + type: 'VPN_USERS', + chartColor: '#95de64' }, { id: 21, - type: 'VM_DISK_IO_READ' + type: 'VM_DISK_IO_READ', + chartColor: '#ffe7ba' }, { id: 22, - type: 'VM_DISK_IO_WRITE' + type: 'VM_DISK_IO_WRITE', + chartColor: '#5b8c00' }, { id: 23, - type: 'VM_DISK_BYTES_READ' + type: 'VM_DISK_BYTES_READ', + chartColor: '#0050b3' }, { id: 24, - type: 'VM_DISK_BYTES_WRITE' + type: 'VM_DISK_BYTES_WRITE', + chartColor: '#520339' }, { id: 25, - type: 'VM_SNAPSHOT' + type: 'VM_SNAPSHOT', + chartColor: '#9e1068' }, { id: 26, - type: 'VOLUME_SECONDARY' + type: 'VOLUME_SECONDARY', + chartColor: '#061178' }, { id: 27, - type: 'VM_SNAPSHOT_ON_PRIMARY' + type: 'VM_SNAPSHOT_ON_PRIMARY', + chartColor: '#ad2102' }, { id: 28, - type: 'BACKUP' + type: 'BACKUP', + chartColor: '#00474f' }, { id: 29, - type: 'BUCKET' + type: 'BUCKET', + chartColor: '#13a8a8' }, { id: 30, - type: 'NETWORK' + type: 'NETWORK', + chartColor: '#c75314' }, { id: 31, - type: 'VPC' + type: 'VPC', + chartColor: '#018391' } ] export const getQuotaTypes = () => { return QUOTA_TYPES.sort((a, b) => a.type.localeCompare(b.type)) } + +export const getQuotaTypeByName = (type) => { + return QUOTA_TYPES.find(quotaType => quotaType.type === type) +} diff --git a/ui/src/utils/renderIcon.js b/ui/src/utils/renderIcon.js index 8d982fd4adc0..b92fba28548b 100644 --- a/ui/src/utils/renderIcon.js +++ b/ui/src/utils/renderIcon.js @@ -45,8 +45,9 @@ export default { const props = Object.assign({}, this.props) props.width = '1em' props.height = '1em' - props.class = 'custom-icon' - + if (!this.$attrs.style) { + props.class = 'custom-icon' + } return h('span', { role: 'img', class: 'anticon' }, [ h(this.svgIcon, { ...props }, this.event) ]) diff --git a/ui/src/utils/request.js b/ui/src/utils/request.js index 42b26c9785b1..2317aac04465 100644 --- a/ui/src/utils/request.js +++ b/ui/src/utils/request.js @@ -54,7 +54,7 @@ const err = (error) => { if (response.config && response.config.params && ['forgotPassword', 'listIdps', 'cloudianIsEnabled'].includes(response.config.params.command)) { return } - const originalPath = router.currentRoute.value.fullPath + const originalPath = router.currentRoute.value.path for (const key in response.data) { if (key.includes('response')) { if (response.data[key].errortext.includes('not available for user')) { diff --git a/ui/src/utils/ssvmProbe.js b/ui/src/utils/ssvmProbe.js new file mode 100644 index 000000000000..55690aea8981 --- /dev/null +++ b/ui/src/utils/ssvmProbe.js @@ -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. + +const SSVM_PROBE_TIMEOUT_MS = 5000 +export async function probeSsvmCert (origin) { + const controller = new AbortController() + const timeoutId = setTimeout(() => controller.abort(), SSVM_PROBE_TIMEOUT_MS) + try { + await fetch(origin, { method: 'HEAD', mode: 'no-cors', signal: controller.signal }) + return true + } catch (e) { + return false + } finally { + clearTimeout(timeoutId) + } +} diff --git a/ui/src/utils/units.js b/ui/src/utils/units.js new file mode 100644 index 000000000000..712b4d8426c3 --- /dev/null +++ b/ui/src/utils/units.js @@ -0,0 +1,28 @@ +// 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. + +export const TIME_UNITS = { + millisecond: 1, + second: 1000, + minute: 60 * 1000, + hour: 60 * 60 * 1000, + day: 24 * 60 * 60 * 1000, + week: 7 * 24 * 60 * 60 * 1000, + month: 30 * 24 * 60 * 60 * 1000, + quarter: 91 * 24 * 60 * 60 * 1000, + year: 365 * 24 * 60 * 60 * 1000 +} diff --git a/ui/src/utils/util.js b/ui/src/utils/util.js index 8773f07446e6..94fbaa14c0af 100644 --- a/ui/src/utils/util.js +++ b/ui/src/utils/util.js @@ -15,6 +15,7 @@ // specific language governing permissions and limitations // under the License. +import dayjs from 'dayjs' import semver from 'semver' export function timeFix () { @@ -79,13 +80,13 @@ export function getParsedVersion (version) { return version } -export function toCsv ({ keys = null, data = null, columnDelimiter = ',', lineDelimiter = '\n' }) { - if (data === null || !data.length) { +export function toCsv ({ keys = null, data = null, columnDelimiter = ',', lineDelimiter = '\n', headers = null, dateFormat = undefined }) { + if (data === null || !data.length || keys === null || !keys.filter(key => key !== null && key !== '').length) { return null } let result = '' - result += keys.join(columnDelimiter) + result += (headers || keys).join(columnDelimiter) result += lineDelimiter data.forEach(item => { @@ -93,7 +94,15 @@ export function toCsv ({ keys = null, data = null, columnDelimiter = ',', lineDe if (item[key] === undefined) { item[key] = '' } - result += typeof item[key] === 'string' && item[key].includes(columnDelimiter) ? `"${item[key]}"` : item[key] + + if (typeof item[key] === 'string' && item[key].includes(columnDelimiter)) { + result += `"${item[key]}"` + } else if (dateFormat && dayjs.isDayjs(item[key])) { + result += `"${item[key].format(dateFormat)}"` + } else { + result += item[key] + } + result += columnDelimiter }) result = result.slice(0, -1) @@ -102,3 +111,38 @@ export function toCsv ({ keys = null, data = null, columnDelimiter = ',', lineDe return result } + +export function downloadDataAsCsv ({ data = null, keys = null, headers = null, columnDelimiter = ',', lineDelimiter = '\n', fileName = 'data', dateFormat = undefined }) { + const dataParsed = toCsv({ keys, data, columnDelimiter, lineDelimiter, headers, dateFormat }) + if (dataParsed === null) { + return + } + + const hiddenElement = document.createElement('a') + hiddenElement.href = 'data:text/csv;charset=utf-8,' + encodeURI(dataParsed) + hiddenElement.target = '_blank' + hiddenElement.download = `${fileName}.csv` + hiddenElement.click() + hiddenElement.remove() +} + +export function isValidIPv4Cidr (rule, value) { + return new Promise((resolve, reject) => { + if (!value) { + reject(new Error()) + return + } + const cidrRegex = /^(\d{1,3}\.){3}\d{1,3}\/([0-9]|[1-2][0-9]|3[0-2])$/ + if (!cidrRegex.test(value)) { + reject(new Error('Invalid CIDR format')) + return + } + const ip = value.split('/')[0] + const octets = ip.split('.').map(Number) + if (octets.some(octet => octet < 0 || octet > 255)) { + reject(new Error('Invalid CIDR format')) + return + } + resolve() + }) +} diff --git a/ui/src/views/AutogenView.vue b/ui/src/views/AutogenView.vue index c77e732ec8cb..8b492d507a2e 100644 --- a/ui/src/views/AutogenView.vue +++ b/ui/src/views/AutogenView.vue @@ -17,7 +17,10 @@ - +
@@ -257,8 +268,18 @@

- - +
+ + + + + + +
+ + + v-model:value="form[field.name]" + :optionalKeys="currentAction.mapping?.[field.name]?.optionalKeys || []" /> {{ $t('label.ok') }} @@ -527,7 +558,7 @@
+ !excludedKeys.includes(f.key)) + }, hasSelected () { return this.selectedRowKeys.length > 0 }, @@ -842,6 +912,12 @@ export default { return 'active' } return 'self' + }, + isSubmitDisabled () { + if (this.currentAction?.requireNameConfirmation && !(this.currentAction.groupAction && this.selectedRowKeys.length > 0)) { + return this.actionConfirmText.trim() !== this.resource?.name?.trim() + } + return false } }, methods: { @@ -851,19 +927,6 @@ export default { } return 'inline-flex' }, - getOkProps () { - if (this.selectedRowKeys.length > 0 && this.currentAction?.groupAction) { - } else { - return { props: { type: 'primary' } } - } - }, - getCancelProps () { - if (this.selectedRowKeys.length > 0 && this.currentAction?.groupAction) { - return { props: { type: 'primary' } } - } else { - return { props: { type: 'default' } } - } - }, switchProject (projectId) { if (!projectId || !projectId.length || projectId.length !== 36) { return @@ -948,7 +1011,7 @@ export default { this.projectView = Boolean(store.getters.project && store.getters.project.id) this.hasProjectId = ['vm', 'vmgroup', 'ssh', 'affinitygroup', 'userdata', 'volume', 'snapshot', 'buckets', 'vmsnapshot', 'guestnetwork', 'vpc', 'securitygroups', 'publicip', 'vpncustomergateway', 'template', 'iso', 'event', 'kubernetes', 'sharedfs', - 'autoscalevmgroup', 'vnfapp', 'webhook'].includes(this.$route.name) + 'autoscalevmgroup', 'vnfapp', 'webhook', 'kmskey', 'hsmprofile'].includes(this.$route.name) if (this.dataView && !refreshed) { this.resource = {} @@ -1004,7 +1067,7 @@ export default { const customRender = {} for (var columnKey of this.columnKeys) { let key = columnKey - let title = columnKey === 'cidr' && this.columnKeys.includes('ip6cidr') ? 'ipv4.cidr' : columnKey + let title = columnKey === 'cidr' && this.columnKeys.includes('ip6cidr') ? 'ipv4.cidr' : key if (typeof columnKey === 'object') { if ('customTitle' in columnKey && 'field' in columnKey) { key = columnKey.field @@ -1012,7 +1075,7 @@ export default { customRender[key] = columnKey[key] } else { key = Object.keys(columnKey)[0] - title = Object.keys(columnKey)[0] + title = (typeof title === 'object') ? key : title customRender[key] = columnKey[key] } } @@ -1057,10 +1120,16 @@ export default { params.details = 'group,nics,secgrp,tmpl,servoff,diskoff,iso,volume,affgrp,backoff' } + if (this.apiName === 'quotaTariffList' && !('quotaTariffCreate' in store.getters.apis || 'quotaTariffUpdate' in store.getters.apis)) { + const index = this.columns.findIndex(col => col.dataIndex === 'hasActivationRule') + if (index >= 0) { + this.columns.splice(index, 1) + } + } + this.loading = true if (this.$route.path.startsWith('/cniconfiguration')) { params.forcks = true - console.log('here') } if (this.$route.params && this.$route.params.id) { params.id = this.$route.params.id @@ -1073,13 +1142,15 @@ export default { params.name = this.$route.params.id } } + if (['listUserKeys'].includes(this.apiName)) { + delete params.listall + params.keypairid = this.$route.params.id + } if (['listPublicIpAddresses'].includes(this.apiName)) { params.allocatedonly = false } if (this.$route.path.startsWith('/vmsnapshot/')) { params.vmsnapshotid = this.$route.params.id - } else if (this.$route.path.startsWith('/ldapsetting/')) { - params.hostname = this.$route.params.id } if (this.$route.path.startsWith('/tungstenpolicy/')) { params.policyuuid = this.$route.params.id @@ -1096,6 +1167,14 @@ export default { if (this.$route.path.startsWith('/tungstenfirewallpolicy/')) { params.firewallpolicyuuid = this.$route.params.id } + if (this.apiName === 'quotaSummary' && params.id) { + params.accountid = params.id + delete params.id + } + if (this.apiName === 'quotaEmailTemplateList' && params.id) { + params.templatetype = params.id + delete params.id + } } if (this.$store.getters.listAllProjects && !this.projectView) { @@ -1142,7 +1221,11 @@ export default { break } - if ('id' in this.$route.params && this.$route.params.id !== params.id) { + const idFromRouteMatchesApiParameter = this.$route.params.id === params.id || + this.apiName === 'quotaSummary' && this.$route.params.id === params.accountid || + this.apiName === 'quotaEmailTemplateList' && this.$route.params.id === params.templatetype + + if ('id' in this.$route.params && !idFromRouteMatchesApiParameter) { console.log('DEBUG - Discarding API response as its `id` does not match the uuid on the browser path') return } @@ -1184,6 +1267,16 @@ export default { }) } + if (this.apiName === 'listBackups') { + const kbossFields = ['compressionstatus', 'validationstatus'] + const hasKbossData = this.items.some(backup => kbossFields.some(field => backup[field])) + if (!hasKbossData) { + this.columns = this.columns.filter(col => !kbossFields.includes(col.dataIndex)) + this.allColumns = this.allColumns.filter(col => !kbossFields.includes(col.dataIndex)) + this.selectedColumns = this.selectedColumns.filter(key => !kbossFields.includes(key)) + } + } + for (let idx = 0; idx < this.items.length; idx++) { this.items[idx].key = idx for (const key in customRender) { @@ -1192,19 +1285,13 @@ export default { this.items[idx][key] = func(this.items[idx]) } } - if (this.$route.path.startsWith('/ldapsetting')) { - this.items[idx].id = this.items[idx].hostname - } } - if (this.items.length > 0) { - if (!this.showAction || this.dataView) { - this.resource = this.items[0] - this.$emit('change-resource', this.resource) - } - } else { - if (this.dataView) { - this.$router.push({ path: '/exception/404' }) - } + if (this.items.length <= 0 && this.dataView) { + this.$router.push({ path: '/exception/404' }) + } + if (!this.showAction || this.dataView || (this.items.length === 1 && this.apiName === 'getUserKeys')) { + this.resource = this.items?.[0] || {} + this.$emit('change-resource', this.resource) } }).catch(error => { if (!error || !error.message) { @@ -1260,11 +1347,24 @@ export default { this.actionLoading = false this.showAction = false this.currentAction = {} + this.actionConfirmText = '' }, cancelAction () { eventBus.emit('action-closing', { action: this.currentAction }) this.closeAction() }, + removeFilter (filter) { + const queryParams = Object.assign({}, this.$route.query) + if (filter.isTag) { + delete queryParams[`tags[${filter.tagIdx}].key`] + delete queryParams[`tags[${filter.tagIdx}].value`] + } else { + delete queryParams[filter.key] + } + queryParams.page = '1' + queryParams.pagesize = String(this.pageSize) + this.$router.push({ query: queryParams }) + }, onRowSelectionChange (selection) { this.selectedRowKeys = selection if (selection?.length > 0) { @@ -1305,6 +1405,7 @@ export default { this.currentAction = action this.currentAction.params = store.getters.apis[this.currentAction.api].params this.resource = action.resource + this.actionConfirmText = '' this.$emit('change-resource', this.resource) var paramFields = this.currentAction.params paramFields.sort(function (a, b) { @@ -1319,9 +1420,11 @@ export default { this.currentAction.paramFilters = [] if ('message' in action) { if (typeof action.message === 'function') { - action.message = action.message(action.resource) + action.messageString = action.message(action.resource) + } else { + action.messageString = action.message } - action.message = Array.isArray(action.message) ? this.$t(...action.message) : this.$t(action.message) + action.messageString = Array.isArray(action.messageString) ? this.$t(...action.messageString) : this.$t(action.messageString) } this.getArgs(action, isGroupAction, paramFields) this.getFilters(action, isGroupAction, paramFields) @@ -1394,6 +1497,21 @@ export default { } } }, + handleSelectChange (name, val) { + if (name === 'domainid') { + const accountField = this.currentAction.paramFields.find(f => f.name === 'account') + if (accountField) { + this.form.account = null + this.listUuidOpts(accountField, { domainid: val }) + } + } else if (name === 'account') { + const volumeField = this.currentAction.paramFields.find(f => f.name === 'volumeids') + if (volumeField) { + this.form.volumeids = null + this.listUuidOpts(volumeField, { domainid: this.form.domainid, account: val }) + } + } + }, listUuidOpts (param, filters) { if (this.currentAction.mapping && param.name in this.currentAction.mapping && !this.currentAction.mapping[param.name].api) { return @@ -1439,9 +1557,16 @@ export default { if (possibleApi === 'listTemplates') { params.templatefilter = 'executable' } else if (possibleApi === 'listIsos') { + if (this.$route.path.startsWith('/kubernetesiso')) { + params.bootable = false + } params.isofilter = 'executable' } else if (possibleApi === 'listHosts') { params.type = 'routing' + if (this.currentAction?.api === 'restoreBackup') { + params.resourcestate = 'enabled' + params.state = 'up' + } } else if (possibleApi === 'listNetworkOfferings' && this.resource) { if (this.resource.type) { params.guestiptype = this.resource.type @@ -1584,6 +1709,12 @@ export default { }, handleSubmit (e) { if (this.actionLoading) return + + if (this.currentAction?.requireNameConfirmation && !(this.currentAction.groupAction && this.selectedRowKeys.length > 0)) { + if (this.actionConfirmText.trim() !== this.resource?.name?.trim()) { + return + } + } this.promises = [] if (!this.dataView && this.currentAction.groupAction && this.selectedRowKeys.length > 0) { if (this.selectedRowKeys.length > 0) { @@ -1772,9 +1903,22 @@ export default { params[key] = param.opts[input].name } } else if (param.type === 'map' && typeof input === 'object') { - Object.entries(values.externaldetails).forEach(([key, value]) => { - params[param.name + '[0].' + key] = value - }) + const details = values[key] + if (details && Object.keys(details).length > 0) { + Object.entries(details).forEach(([k, v]) => { + params[key + '[0].' + k] = v + }) + } else { + if (['details', 'externaldetails'].includes(key)) { + const updateApiParams = this.$getApiParams(action.api) + const cleanupKey = 'cleanup' + key + if (cleanupKey in updateApiParams) { + params[cleanupKey] = true + break + } + } + params[key] = {} + } } else { params[key] = input } @@ -1986,8 +2130,12 @@ export default { }, onSearch (opts) { const query = Object.assign({}, this.$route.query) - for (const key in this.searchParams) { - delete query[key] + let searchFilters = this.$route?.meta?.searchFilters || [] + if (typeof searchFilters === 'function') { + searchFilters = searchFilters() + } + if (Array.isArray(searchFilters)) { + searchFilters.forEach(key => delete query[key]) } delete query.name delete query.templatetype diff --git a/ui/src/views/auth/ForgotPassword.vue b/ui/src/views/auth/ForgotPassword.vue index 78af0c3d9f17..a508934070af 100644 --- a/ui/src/views/auth/ForgotPassword.vue +++ b/ui/src/views/auth/ForgotPassword.vue @@ -162,7 +162,7 @@ export default { postAPI('forgotPassword', loginParams) .finally(() => { this.$message.success(this.$t('message.forgot.password.success')) - this.$router.push({ path: '/login' }).catch(() => {}) + this.$router.replace({ path: '/user/login' }) }) }).catch(error => { this.formRef.value.scrollToField(error.errorFields[0].name) diff --git a/ui/src/views/auth/Login.vue b/ui/src/views/auth/Login.vue index 24065f47b1aa..9dcc8b4f34c2 100644 --- a/ui/src/views/auth/Login.vue +++ b/ui/src/views/auth/Login.vue @@ -27,6 +27,7 @@ > + + + + + + + +
+ Enter your domain to see domain-specific providers +
+
+ + + +
+
+ +
+
+ No OAuth providers configured for this domain + Enter your domain to see available providers +
+
- + -
-

or

-
-
- - -
@@ -231,10 +276,26 @@ export default { socialLogin: false, googleprovider: false, githubprovider: false, + keycloakprovider: false, googleredirecturi: '', githubredirecturi: '', + keycloakredirecturi: '', googleclientid: '', githubclientid: '', + keycloakclientid: '', + keycloakauthorizeurl: '', + oauthGoogleProvider: false, + oauthGithubProvider: false, + oauthKeycloakProvider: false, + oauthGoogleClientId: '', + oauthGithubClientId: '', + oauthKeycloakClientId: '', + oauthGoogleRedirectUri: '', + oauthGithubRedirectUri: '', + oauthKeycloakRedirectUri: '', + oauthKeycloakAuthorizeUrl: '', + oauthLoading: false, + oauthDomainQueried: false, loginType: 0, state: { time: 60, @@ -269,6 +330,7 @@ export default { server: (this.server.apiHost || '') + this.server.apiBase, username: this.$route.query?.username || '', domain: this.$route.query?.domain || '', + oauthDomain: '', project: null }) this.rules = reactive({}) @@ -311,24 +373,7 @@ export default { this.form.idp = this.idps[0].id || '' } }) - getAPI('listOauthProvider', {}).then(response => { - if (response) { - const oauthproviders = response.listoauthproviderresponse.oauthprovider || [] - oauthproviders.forEach(item => { - if (item.provider === 'google') { - this.googleprovider = item.enabled - this.googleclientid = item.clientid - this.googleredirecturi = item.redirecturi - } - if (item.provider === 'github') { - this.githubprovider = item.enabled - this.githubclientid = item.clientid - this.githubredirecturi = item.redirecturi - } - }) - this.socialLogin = this.googleprovider || this.githubprovider - } - }) + this.fetchOauthProviders() postAPI('forgotPassword', {}).then(response => { this.forgotPasswordEnabled = response.forgotpasswordresponse.enabled }).catch((err) => { @@ -339,6 +384,74 @@ export default { } }) }, + fetchOauthProviders (domain) { + const params = {} + if (domain) { + params.domain = domain + this.oauthLoading = true + } + getAPI('listOauthProvider', params).then(response => { + if (response) { + const oauthproviders = response.listoauthproviderresponse.oauthprovider || [] + if (!domain) { + oauthproviders.forEach(item => { + if (item.provider === 'google') { + this.googleprovider = item.enabled + this.googleclientid = item.clientid + this.googleredirecturi = item.redirecturi + } + if (item.provider === 'github') { + this.githubprovider = item.enabled + this.githubclientid = item.clientid + this.githubredirecturi = item.redirecturi + } + if (item.provider === 'keycloak') { + this.keycloakprovider = item.enabled + this.keycloakclientid = item.clientid + this.keycloakredirecturi = item.redirecturi + this.keycloakauthorizeurl = item.authorizeurl + } + }) + const totalCount = response.listoauthproviderresponse.count || 0 + this.socialLogin = totalCount > 0 + this.oauthGithubProvider = this.githubprovider + this.oauthGoogleProvider = this.googleprovider + this.oauthKeycloakProvider = this.keycloakprovider + this.oauthGithubClientId = this.githubclientid + this.oauthGoogleClientId = this.googleclientid + this.oauthKeycloakClientId = this.keycloakclientid + this.oauthGithubRedirectUri = this.githubredirecturi + this.oauthGoogleRedirectUri = this.googleredirecturi + this.oauthKeycloakRedirectUri = this.keycloakredirecturi + this.oauthKeycloakAuthorizeUrl = this.keycloakauthorizeurl + } else { + this.oauthGithubProvider = false + this.oauthGoogleProvider = false + this.oauthKeycloakProvider = false + oauthproviders.forEach(item => { + if (item.provider === 'google') { + this.oauthGoogleProvider = item.enabled + this.oauthGoogleClientId = item.clientid + this.oauthGoogleRedirectUri = item.redirecturi + } + if (item.provider === 'github') { + this.oauthGithubProvider = item.enabled + this.oauthGithubClientId = item.clientid + this.oauthGithubRedirectUri = item.redirecturi + } + if (item.provider === 'keycloak') { + this.oauthKeycloakProvider = item.enabled + this.oauthKeycloakClientId = item.clientid + this.oauthKeycloakRedirectUri = item.redirecturi + this.oauthKeycloakAuthorizeUrl = item.authorizeurl + } + }) + } + } + }).finally(() => { + this.oauthLoading = false + }) + }, // handler async handleUsernameOrEmail (rule, value) { const { state } = this @@ -352,8 +465,39 @@ export default { }, handleTabClick (key) { this.customActiveKey = key + if (key === 'oauth') { + this.oauthGithubProvider = this.githubprovider + this.oauthGoogleProvider = this.googleprovider + this.oauthKeycloakProvider = this.keycloakprovider + this.oauthGithubClientId = this.githubclientid + this.oauthGoogleClientId = this.googleclientid + this.oauthKeycloakClientId = this.keycloakclientid + this.oauthGithubRedirectUri = this.githubredirecturi + this.oauthGoogleRedirectUri = this.googleredirecturi + this.oauthKeycloakRedirectUri = this.keycloakredirecturi + this.oauthKeycloakAuthorizeUrl = this.keycloakauthorizeurl + } this.setRules() }, + handleOauthDomainSubmit () { + const domain = this.form.oauthDomain + if (domain) { + this.oauthDomainQueried = true + this.fetchOauthProviders(domain) + } else { + this.oauthDomainQueried = false + this.oauthGithubProvider = this.githubprovider + this.oauthGoogleProvider = this.googleprovider + this.oauthKeycloakProvider = this.keycloakprovider + this.oauthGithubClientId = this.githubclientid + this.oauthGoogleClientId = this.googleclientid + this.oauthKeycloakClientId = this.keycloakclientid + this.oauthGithubRedirectUri = this.githubredirecturi + this.oauthGoogleRedirectUri = this.googleredirecturi + this.oauthKeycloakRedirectUri = this.keycloakredirecturi + this.oauthKeycloakAuthorizeUrl = this.keycloakauthorizeurl + } + }, handleGithubProviderAndDomain () { this.handleDomain() this.$store.commit('SET_OAUTH_PROVIDER_USED_TO_LOGIN', 'github') @@ -362,18 +506,32 @@ export default { this.handleDomain() this.$store.commit('SET_OAUTH_PROVIDER_USED_TO_LOGIN', 'google') }, + handleKeycloakProviderAndDomain () { + this.handleDomain() + this.$store.commit('SET_OAUTH_PROVIDER_USED_TO_LOGIN', 'keycloak') + }, handleDomain () { const values = toRaw(this.form) - if (!values.domain) { - this.$store.commit('SET_DOMAIN_USED_TO_LOGIN', '/') - } else { - this.$store.commit('SET_DOMAIN_USED_TO_LOGIN', values.domain) + const domain = this.customActiveKey === 'oauth' ? values.oauthDomain : values.domain + this.$store.commit('SET_DOMAIN_USED_TO_LOGIN', this.getLoginDomain(domain)) + }, + getLoginDomain (domain) { + if (this.$config.loginBaseDomain) { + if (domain) { + return this.$config.loginBaseDomain + '/' + domain + } + return this.$config.loginBaseDomain } + if (domain) { + return domain + } + return '/' }, getGitHubUrl (from) { const rootURl = 'https://github.com/login/oauth/authorize' + const clientId = this.customActiveKey === 'oauth' ? this.oauthGithubClientId : this.githubclientid const options = { - client_id: this.githubclientid, + client_id: clientId, scope: 'user:email', state: 'cloudstack' } @@ -384,9 +542,11 @@ export default { }, getGoogleUrl (from) { const rootUrl = 'https://accounts.google.com/o/oauth2/v2/auth' + const redirectUri = this.customActiveKey === 'oauth' ? this.oauthGoogleRedirectUri : this.googleredirecturi + const clientId = this.customActiveKey === 'oauth' ? this.oauthGoogleClientId : this.googleclientid const options = { - redirect_uri: this.googleredirecturi, - client_id: this.googleclientid, + redirect_uri: redirectUri, + client_id: clientId, access_type: 'offline', response_type: 'code', prompt: 'consent', @@ -401,6 +561,22 @@ export default { return `${rootUrl}?${qs.toString()}` }, + getKeycloakUrl (from) { + const rootURl = this.customActiveKey === 'oauth' ? this.oauthKeycloakAuthorizeUrl : this.keycloakauthorizeurl + const redirectUri = this.customActiveKey === 'oauth' ? this.oauthKeycloakRedirectUri : this.keycloakredirecturi + const clientId = this.customActiveKey === 'oauth' ? this.oauthKeycloakClientId : this.keycloakclientid + const options = { + redirect_uri: redirectUri, + client_id: clientId, + response_type: 'code', + scope: 'openid email', + state: 'cloudstack' + } + + const qs = new URLSearchParams(options) + + return `${rootURl}?${qs.toString()}` + }, handleSubmit (e) { e.preventDefault() if (this.state.loginBtn) return @@ -417,10 +593,7 @@ export default { delete loginParams.username loginParams[!this.state.loginType ? 'email' : 'username'] = values.username loginParams.password = values.password - loginParams.domain = values.domain - if (!loginParams.domain) { - loginParams.domain = '/' - } + loginParams.domain = this.getLoginDomain(values.domain) this.Login(loginParams) .then((res) => this.loginSuccess(res)) .catch(err => { @@ -449,10 +622,7 @@ export default { loginParams.email = this.email loginParams.provider = provider loginParams.secretcode = this.secretcode - loginParams.domain = values.domain - if (!loginParams.domain) { - loginParams.domain = '/' - } + loginParams.domain = this.getLoginDomain(values.domain) this.OauthLogin(loginParams) .then((res) => this.loginSuccess(res)) .catch(err => { @@ -519,12 +689,21 @@ export default { diff --git a/ui/src/views/compute/CreateAffinityGroup.vue b/ui/src/views/compute/CreateAffinityGroup.vue new file mode 100644 index 000000000000..27d9828138d4 --- /dev/null +++ b/ui/src/views/compute/CreateAffinityGroup.vue @@ -0,0 +1,173 @@ +// 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/CreateAutoScaleVmGroup.vue b/ui/src/views/compute/CreateAutoScaleVmGroup.vue index 362c9b7b4063..9e8ebb8a4d59 100644 --- a/ui/src/views/compute/CreateAutoScaleVmGroup.vue +++ b/ui/src/views/compute/CreateAutoScaleVmGroup.vue @@ -3172,12 +3172,12 @@ export default { configuration.cpunumber = 0 configuration.cpuspeed = 0 configuration.memory = 0 - for (var harwareItem of configuration.hardwareItems) { - if (harwareItem.resourceType === 'Processor') { - configuration.cpunumber = harwareItem.virtualQuantity - configuration.cpuspeed = harwareItem.reservation - } else if (harwareItem.resourceType === 'Memory') { - configuration.memory = harwareItem.virtualQuantity + for (var hardwareItem of configuration.hardwareItems) { + if (hardwareItem.resourceType === 'Processor') { + configuration.cpunumber = hardwareItem.virtualQuantity + configuration.cpuspeed = hardwareItem.reservation + } else if (hardwareItem.resourceType === 'Memory') { + configuration.memory = hardwareItem.virtualQuantity } } configurations.push(configuration) diff --git a/ui/src/views/compute/CreateKubernetesCluster.vue b/ui/src/views/compute/CreateKubernetesCluster.vue index f945da732138..618e46cb72d9 100644 --- a/ui/src/views/compute/CreateKubernetesCluster.vue +++ b/ui/src/views/compute/CreateKubernetesCluster.vue @@ -65,6 +65,7 @@
+ + + + + + + + @@ -743,6 +772,12 @@
+ + + + import { ref, reactive, toRaw, nextTick, h } from 'vue' -import { Button } from 'ant-design-vue' +import { Button, message } from 'ant-design-vue' import { getAPI, postAPI } from '@/api' +import { isAdmin } from '@/role' import _ from 'lodash' import { mixin, mixinDevice } from '@/utils/mixin.js' import store from '@/store' @@ -930,6 +966,7 @@ import SecurityGroupSelection from '@views/compute/wizard/SecurityGroupSelection import TooltipLabel from '@/components/widgets/TooltipLabel' import InstanceNicsNetworkSelectListView from '@/components/view/InstanceNicsNetworkSelectListView' import DetailsInput from '@/components/widgets/DetailsInput' +import DeployInstanceBackupSelection from '@views/compute/wizard/DeployInstanceBackupSelection' export default { name: 'Wizard', @@ -955,7 +992,8 @@ export default { SecurityGroupSelection, TooltipLabel, InstanceNicsNetworkSelectListView, - DetailsInput + DetailsInput, + DeployInstanceBackupSelection }, props: { visible: { @@ -1022,7 +1060,8 @@ export default { keyboards: [], bootTypes: [], bootModes: [], - ioPolicyTypes: [] + ioPolicyTypes: [], + kmsKeys: [] }, rowCount: {}, loading: { @@ -1043,7 +1082,8 @@ export default { pods: false, clusters: false, hosts: false, - groups: false + groups: false, + kmsKeys: false }, owner: { projectid: store.getters.project?.id, @@ -1135,7 +1175,10 @@ export default { opts: [] }, externalDetailsEnabled: false, - selectedExtensionId: null + selectedExtensionId: null, + zoneAllowsBackupOperations: false, + selectedBackupOffering: null, + backupSchedules: [] } }, computed: { @@ -1202,6 +1245,7 @@ export default { return _.map(this.affinityGroups, 'id') }, params () { + const listAll = isAdmin() return { serviceOfferings: { list: 'listServiceOfferings', @@ -1249,7 +1293,7 @@ export default { domainid: this.owner.domainid, projectid: this.owner.projectid, keyword: undefined, - listall: false + listall: listAll } }, sshKeyPairs: { @@ -1257,8 +1301,11 @@ export default { options: { page: 1, pageSize: 10, + account: this.owner.account, + domainid: this.owner.domainid, + projectid: this.owner.projectid, keyword: undefined, - listall: false + listall: listAll } }, userDatas: { @@ -1266,8 +1313,11 @@ export default { options: { page: 1, pageSize: 10, + account: this.owner.account, + domainid: this.owner.domainid, + projectid: this.owner.projectid, keyword: undefined, - listall: false + listall: listAll } }, networks: { @@ -1463,6 +1513,9 @@ export default { return tabList }, showSecurityGroupSection () { + if (this.zone && this.zone.networktype === 'Basic') { + return true + } if (this.networks.length < 1) { return false } @@ -1486,6 +1539,9 @@ export default { dynamicScalingVmConfigValue () { return this.$store.getters.features.dynamicscalingenabled }, + extraConfigEnabled () { + return this.$store.getters.features.additionalconfigenabled + }, isCustomizedDiskIOPS () { return this.diskSelected?.iscustomizediops || false }, @@ -1672,12 +1728,35 @@ export default { if (this.leaseduration < 1) { this.vm.leaseduration = undefined } + + delete this.vm.backupofferingid + delete this.vm.backupofferingname + if (this.form.backupofferingid && this.selectedBackupOffering) { + this.vm.backupofferingid = this.selectedBackupOffering.id + this.vm.backupofferingname = this.selectedBackupOffering.name + } } } }, serviceOffering (oldValue, newValue) { if (oldValue && newValue && oldValue.id !== newValue.id) { this.dynamicscalingenabled = this.isDynamicallyScalable() + // Fetch KMS keys if encryption is enabled + if (newValue && newValue.encryptroot && this.zoneId) { + this.fetchKmsKeys() + } + } + }, + diskOffering (newValue) { + // Fetch KMS keys if encryption is enabled + if (newValue && newValue.encrypt && this.zoneId) { + this.fetchKmsKeys() + } + }, + overrideDiskOffering (newValue) { + // Fetch KMS keys if encryption is enabled + if (newValue && newValue.encrypt && this.zoneId) { + this.fetchKmsKeys() } }, template (oldValue, newValue) { @@ -1872,7 +1951,7 @@ export default { } this.fetchBootTypes() this.fetchBootModes() - this.fetchInstaceGroups() + this.fetchInstanceGroups() this.fetchIoPolicyTypes() nextTick().then(() => { ['name', 'keyboard', 'boottype', 'bootmode', 'userdata', 'iothreadsenabled', 'iodriverpolicy', 'nicmultiqueuenumber', 'nicpackedvirtqueues'].forEach(this.fillValue) @@ -1928,7 +2007,7 @@ export default { { id: 'storage_specific', description: 'storage_specific' } ] }, - fetchInstaceGroups () { + fetchInstanceGroups () { this.options.instanceGroups = [] getAPI('listInstanceGroups', { account: this.$store.getters.project?.id ? null : this.$store.getters.userInfo.account, @@ -1945,6 +2024,31 @@ export default { const param = this.params.networks this.fetchOptions(param, 'networks') }, + fetchKmsKeys () { + if (!this.zoneId) { + return + } + this.loading.kmsKeys = true + this.options.kmsKeys = [] + getAPI('listKMSKeys', { + zoneid: this.zoneId, + account: this.owner.account, + domainid: this.owner.domainid, + projectid: this.owner.projectid, + purpose: 'volume' + }).then(response => { + const kmskeyMap = response.listkmskeysresponse.kmskey || [] + if (kmskeyMap.length > 0) { + this.options.kmsKeys = kmskeyMap + } else { + this.options.kmsKeys = null + } + }).catch(() => { + this.options.kmsKeys = null + }).finally(() => { + this.loading.kmsKeys = false + }) + }, resetData () { this.vm = { name: null, @@ -1969,6 +2073,12 @@ export default { this.formRef.value.resetFields() this.fetchData() }, + updateRootKmsKey (value) { + this.form.rootkmskeyid = value + }, + updateDataKmsKey (value) { + this.form.datakmskeyid = value + }, updateFieldValue (name, value) { if (name === 'templateid') { this.imageType = 'templateid' @@ -2292,6 +2402,9 @@ export default { if (isUserdataAllowed && values.userdata && values.userdata.length > 0) { deployVmData.userdata = this.$toBase64AndURIEncoded(values.userdata) } + if (values.extraconfig && values.extraconfig.length > 0) { + deployVmData.extraconfig = encodeURIComponent(values.extraconfig) + } // step 2: select template/iso if (this.imageType === 'templateid') { deployVmData.templateid = values.templateid @@ -2329,6 +2442,10 @@ export default { deployVmData['details[0].memory'] = values.memory } } + // Add root disk KMS key if selected (optional - falls back to legacy passphrase if not provided) + if (values.rootkmskeyid) { + deployVmData.rootdiskkmskeyid = values.rootkmskeyid + } if (this.selectedTemplateConfiguration) { deployVmData['details[0].configurationId'] = this.selectedTemplateConfiguration.id } @@ -2355,12 +2472,29 @@ export default { }) } } else { - deployVmData.diskofferingid = values.diskofferingid - if (values.size) { - deployVmData.size = values.size + // When a KMS key is selected for data disk, we must use datadisksdetails format + if (values.datakmskeyid) { + deployVmData['datadisksdetails[0].diskofferingid'] = values.diskofferingid + deployVmData['datadisksdetails[0].deviceid'] = 1 // Device ID 1 for first data disk (0=root, 3=CD-ROM reserved) + if (values.size) { + deployVmData['datadisksdetails[0].size'] = values.size + } + deployVmData['datadisksdetails[0].kmskeyid'] = values.datakmskeyid + // Add IOPS if customized + if (this.isCustomizedDiskIOPS) { + deployVmData['datadisksdetails[0].miniops'] = this.diskIOpsMin + deployVmData['datadisksdetails[0].maxiops'] = this.diskIOpsMax + } + } else { + // Legacy format when no KMS key + deployVmData.diskofferingid = values.diskofferingid + if (values.size) { + deployVmData.size = values.size + } } } - if (this.isCustomizedDiskIOPS) { + // IOPS for non-KMS data disks (KMS data disks IOPS handled above in datadisksdetails) + if (this.isCustomizedDiskIOPS && !values.datakmskeyid) { deployVmData['details[0].minIopsDo'] = this.diskIOpsMin deployVmData['details[0].maxIopsDo'] = this.diskIOpsMax } @@ -2507,6 +2641,7 @@ export default { duration: 0 }) } + this.performPostDeployBackupActions(vm) eventBus.emit('vm-refresh-data') }, loadingMessage: `${title} ${this.$t('label.in.progress')}`, @@ -2569,7 +2704,9 @@ export default { this.owner.domainid = null this.owner.projectid = OwnerOptions.selectedProject } - this.resetData() + if (OwnerOptions.initialized) { + this.resetData() + } }, fetchZones (zoneId, listZoneAllow) { this.zones = [] @@ -2979,6 +3116,31 @@ export default { this.updateTemplateKey() this.formModel = toRaw(this.form) }, + async updateZoneAllowsBackupOperations () { + this.zoneAllowsBackupOperations = false + if (!this.zoneId) { + return + } + if (!('listBackupOfferings' in this.$store.getters.apis) || + !('assignVirtualMachineToBackupOffering' in this.$store.getters.apis)) { + return + } + const params = { + zoneid: this.zoneId, + issystem: false, + listall: true, + page: 1, + pageSize: 1 + } + try { + const response = await getAPI('listBackupOfferings', params) + const backupOfferings = response.listbackupofferingsresponse.backupoffering || [] + this.zoneAllowsBackupOperations = backupOfferings.length > 0 + } catch (error) { + console.error('Error fetching backup offerings:', error) + this.zoneAllowsBackupOperations = false + } + }, onSelectZoneId (value) { if (this.dataPreFill.zoneid !== value) { this.dataPreFill = {} @@ -3004,7 +3166,11 @@ export default { this.resetTemplatesList() this.resetIsosList() this.imageType = this.queryIsoId ? 'isoid' : 'templateid' + this.form.backupofferingid = undefined + this.selectedBackupOffering = null this.fetchZoneOptions() + this.updateZoneAllowsBackupOperations() + this.fetchKmsKeys() }, onSelectPodId (value) { this.podId = value @@ -3144,12 +3310,12 @@ export default { configuration.cpunumber = 0 configuration.cpuspeed = 0 configuration.memory = 0 - for (var harwareItem of configuration.hardwareItems) { - if (harwareItem.resourceType === 'Processor') { - configuration.cpunumber = harwareItem.virtualQuantity - configuration.cpuspeed = harwareItem.reservation - } else if (harwareItem.resourceType === 'Memory') { - configuration.memory = harwareItem.virtualQuantity + for (var hardwareItem of configuration.hardwareItems) { + if (hardwareItem.resourceType === 'Processor') { + configuration.cpunumber = hardwareItem.virtualQuantity + configuration.cpuspeed = hardwareItem.reservation + } else if (hardwareItem.resourceType === 'Memory') { + configuration.memory = hardwareItem.virtualQuantity } } configurations.push(configuration) @@ -3395,6 +3561,113 @@ export default { return } this.form.externaldetails = undefined + }, + onChangeBackupOffering (val) { + if (!val || !val.id) { + this.selectedBackupOffering = null + this.backupSchedules = [] + return + } + this.selectedBackupOffering = val + if (this.backupSchedules && this.backupSchedules.length > 0 && !this.$isBackupProviderSupportsQuiesceVm(val.provider)) { + this.backupSchedules = this.backupSchedules.filter(item => !item.quiescevm) + } + }, + onAddBackupSchedule (schedule) { + if (!schedule) { + return + } + // This is in accordance with the API behavior that only one schedule per intervaltype is allowed + const existingIndex = this.backupSchedules.findIndex(item => item.intervaltype === schedule.intervaltype) + if (existingIndex !== -1) { + message.warning({ + content: this.$t('message.backup.update.existing.schedule') + ' ' + schedule.intervaltype, + duration: 2 + }) + this.backupSchedules.splice(existingIndex, 1, schedule) + return + } + this.backupSchedules.push(schedule) + }, + async performPostDeployBackupActions (vm) { + if (!this.zoneAllowsBackupOperations) { + return + } + const assigned = await this.assignVirtualMachineToBackupOfferingIfNeeded(vm) + if (assigned) { + await this.createVirtualMachineBackupSchedulesIfNeeded(vm) + } + }, + assignVirtualMachineToBackupOfferingIfNeeded (vm) { + if (!this.form.backupofferingid || !vm || !vm.id) { + return Promise.resolve(false) + } + const params = { + virtualmachineid: vm.id, + backupofferingid: this.form.backupofferingid + } + return new Promise((resolve, reject) => { + postAPI('assignVirtualMachineToBackupOffering', params).then(json => { + const jobId = json.assignvirtualmachinetobackupofferingresponse?.jobid + if (!jobId) { + resolve(false) + return + } + this.$pollJob({ + jobId, + loadingMessage: `${this.$t('label.backup.offering.assign')} ${this.$t('label.in.progress')}`, + successMethod: () => { + resolve(true) + }, + errorMethod: (result) => { + this.$notification.error({ + message: this.$t('label.backup.offering.assign.failed'), + description: result?.jobresult?.errortext || this.$t('error.fetching.async.job.result') + }) + resolve(false) + }, + catchMessage: this.$t('error.fetching.async.job.result') + }) + }).catch(error => { + this.$notification.error({ + message: this.$t('label.backup.offering.assign.failed'), + description: error.message || error + }) + resolve(false) + }) + }) + }, + createVirtualMachineBackupSchedulesIfNeeded (vm) { + if (!vm || !vm.id || !this.backupSchedules) { + return Promise.resolve() + } + const promises = (this.backupSchedules || []).map(item => + this.createVirtualMachineBackupSchedule(vm, item) + ) + return Promise.all(promises) + }, + createVirtualMachineBackupSchedule (vm, item) { + const params = { + virtualmachineid: vm.id, + intervaltype: item.intervaltype, + maxbackups: item.maxbackups, + timezone: item.timezone, + schedule: item.schedule + } + if (item.quiescevm) { + params.quiescevm = item.quiescevm + } + return new Promise((resolve, reject) => { + postAPI('createBackupSchedule', params).then(response => { + resolve(response) + }).catch(error => { + this.$notification.error({ + message: this.$t('label.backup.schedule.create.failed'), + description: error.message || error + }) + reject(error) + }) + }) } } } diff --git a/ui/src/views/compute/DeployVnfAppliance.vue b/ui/src/views/compute/DeployVnfAppliance.vue index d040530eb76c..05f804ef4eac 100644 --- a/ui/src/views/compute/DeployVnfAppliance.vue +++ b/ui/src/views/compute/DeployVnfAppliance.vue @@ -272,7 +272,7 @@ @@ -61,7 +60,6 @@ @@ -70,7 +68,6 @@ - - - - - - - - - -
- {{ $t('label.cancel') }} - {{ $t('label.ok') }} -
-
-
- - - - - diff --git a/ui/src/views/plugins/quota/EmailTemplateDetails.vue b/ui/src/views/plugins/quota/EmailTemplateDetails.vue index 0644fa55b69d..a048cdd7188d 100644 --- a/ui/src/views/plugins/quota/EmailTemplateDetails.vue +++ b/ui/src/views/plugins/quota/EmailTemplateDetails.vue @@ -63,9 +63,14 @@ import { postAPI } from '@/api' export default { name: 'EmailTemplateDetails', + props: { + resource: { + type: Object, + required: true + } + }, data () { return { - resource: {}, formModel: { templatesubject: null, templatebody: null @@ -74,29 +79,10 @@ export default { } }, created () { - this.fetchData() + this.formModel.templatesubject = this.resource.templatesubject || null + this.formModel.templatebody = this.resource.templatebody || null }, methods: { - fetchData () { - this.loading = true - const params = {} - params.templatetype = this.$route.params.id - - postAPI('quotaEmailTemplateList', params).then(json => { - const listTemplates = json.quotaemailtemplatelistresponse.quotaemailtemplate || [] - this.resource = listTemplates && listTemplates.length > 0 ? listTemplates[0] : {} - this.preFillDataValues() - }).catch(e => { - this.$notifyError(e) - }).finally(() => { - this.loading = false - }) - }, - preFillDataValues () { - console.log(this.resource) - this.formModel.templatesubject = this.resource.templatesubject || null - this.formModel.templatebody = this.resource.templatebody || null - }, handleSubmit () { if (this.loading) return const params = {} diff --git a/ui/src/views/plugins/quota/FilterQuotaDataByPeriodView.vue b/ui/src/views/plugins/quota/FilterQuotaDataByPeriodView.vue new file mode 100644 index 000000000000..3e4839b21aa2 --- /dev/null +++ b/ui/src/views/plugins/quota/FilterQuotaDataByPeriodView.vue @@ -0,0 +1,161 @@ +// 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/plugins/quota/QuotaBalance.vue b/ui/src/views/plugins/quota/QuotaBalance.vue deleted file mode 100644 index f1cf640f6aaa..000000000000 --- a/ui/src/views/plugins/quota/QuotaBalance.vue +++ /dev/null @@ -1,173 +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. - - - - diff --git a/ui/src/views/plugins/quota/QuotaBalanceTab.vue b/ui/src/views/plugins/quota/QuotaBalanceTab.vue new file mode 100644 index 000000000000..46c2e03d8fe9 --- /dev/null +++ b/ui/src/views/plugins/quota/QuotaBalanceTab.vue @@ -0,0 +1,204 @@ +// 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/plugins/quota/QuotaCreditTab.vue b/ui/src/views/plugins/quota/QuotaCreditTab.vue new file mode 100644 index 000000000000..3ae32a02d526 --- /dev/null +++ b/ui/src/views/plugins/quota/QuotaCreditTab.vue @@ -0,0 +1,230 @@ +// 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/plugins/quota/QuotaSummary.vue b/ui/src/views/plugins/quota/QuotaSummary.vue deleted file mode 100644 index 5bfa72a8abae..000000000000 --- a/ui/src/views/plugins/quota/QuotaSummary.vue +++ /dev/null @@ -1,65 +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. - - - - diff --git a/ui/src/views/plugins/quota/QuotaSummaryResource.vue b/ui/src/views/plugins/quota/QuotaSummaryResource.vue deleted file mode 100644 index c6acf30676fc..000000000000 --- a/ui/src/views/plugins/quota/QuotaSummaryResource.vue +++ /dev/null @@ -1,98 +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. - - - - - - diff --git a/ui/src/views/plugins/quota/QuotaUsage.vue b/ui/src/views/plugins/quota/QuotaUsage.vue deleted file mode 100644 index 6c55a728bd1c..000000000000 --- a/ui/src/views/plugins/quota/QuotaUsage.vue +++ /dev/null @@ -1,158 +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. - - - - diff --git a/ui/src/views/plugins/quota/QuotaUsageTab.vue b/ui/src/views/plugins/quota/QuotaUsageTab.vue new file mode 100644 index 000000000000..cefc169c8d4a --- /dev/null +++ b/ui/src/views/plugins/quota/QuotaUsageTab.vue @@ -0,0 +1,731 @@ +// 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/project/iam/ProjectRoleTab.vue b/ui/src/views/project/iam/ProjectRoleTab.vue index c3a3159565dd..406952a562ae 100644 --- a/ui/src/views/project/iam/ProjectRoleTab.vue +++ b/ui/src/views/project/iam/ProjectRoleTab.vue @@ -144,7 +144,7 @@ export default { createModalVisible: false, editModalVisible: false, selectedRole: null, - projectPermisssions: [], + projectPermissions: [], customStyle: 'margin-bottom: 0; border: none' } }, diff --git a/ui/src/views/setting/ConfigurationHierarchy.vue b/ui/src/views/setting/ConfigurationHierarchy.vue index 80b464e657cc..815a048bc257 100644 --- a/ui/src/views/setting/ConfigurationHierarchy.vue +++ b/ui/src/views/setting/ConfigurationHierarchy.vue @@ -34,7 +34,7 @@ {{ record.description }} @@ -83,6 +83,9 @@ export default { return 'light-row' } return 'dark-row' + }, + handleConfigRefresh (name, updatedRecord) { + this.$emit('refresh-config', name, updatedRecord) } } } diff --git a/ui/src/views/setting/ConfigurationTab.vue b/ui/src/views/setting/ConfigurationTab.vue index 75905cbd1748..65b256c94c95 100644 --- a/ui/src/views/setting/ConfigurationTab.vue +++ b/ui/src/views/setting/ConfigurationTab.vue @@ -58,7 +58,8 @@ :count="count" :page="page" :pagesize="pagesize" - @change-page="changePage" /> + @change-page="changePage" + @refresh-config="handleConfigRefresh" /> + :config="config" + @refresh-config="handleConfigRefresh" /> @@ -322,6 +324,13 @@ export default { '#' + this.$route.path ) } + }, + handleConfigRefresh (name, updatedRecord) { + if (!name || !updatedRecord) return + const index = this.config.findIndex(item => item.name === name) + if (index !== -1) { + this.config.splice(index, 1, updatedRecord) + } } } } diff --git a/ui/src/views/setting/ConfigurationTable.vue b/ui/src/views/setting/ConfigurationTable.vue index 56518d2570b1..7edc1b1aad62 100644 --- a/ui/src/views/setting/ConfigurationTable.vue +++ b/ui/src/views/setting/ConfigurationTable.vue @@ -32,7 +32,10 @@ {{record.displaytext }} {{ ' (' + record.name + ')' }}
{{ record.description }} @@ -85,6 +88,10 @@ export default { pagesize: { type: Number, default: 20 + }, + resource: { + type: Object, + required: false } }, data () { @@ -109,6 +116,9 @@ export default { return 'config-light-row' } return 'config-dark-row' + }, + handleConfigRefresh (name, updatedRecord) { + this.$emit('refresh-config', name, updatedRecord) } } } diff --git a/ui/src/views/setting/ConfigurationValue.vue b/ui/src/views/setting/ConfigurationValue.vue index e438f0eb8315..0979c3c03cbe 100644 --- a/ui/src/views/setting/ConfigurationValue.vue +++ b/ui/src/views/setting/ConfigurationValue.vue @@ -187,7 +187,7 @@ @onClick="$resetConfigurationValueConfirm(configrecord, resetConfigurationValue)" v-if="editableValueKey === null" icon="reload-outlined" - :disabled="(!('resetConfiguration' in $store.getters.apis) || configDisabled || valueLoading)" /> + :disabled="(!('resetConfiguration' in $store.getters.apis) || configDisabled || valueLoading || configrecord.value === configrecord.defaultvalue)" /> @@ -217,6 +217,10 @@ export default { actions: { type: Array, default: () => [] + }, + resource: { + type: Object, + required: false } }, data () { @@ -254,6 +258,12 @@ export default { this.setConfigData() }, watch: { + configrecord: { + handler () { + this.setConfigData() + }, + deep: true + } }, methods: { setConfigData () { @@ -263,6 +273,7 @@ export default { this.editableValueKey = null }, updateConfigurationValue (configrecord) { + let configRecordEntry = this.configrecord this.valueLoading = true this.editableValueKey = null var newValue = this.editableValue @@ -280,21 +291,21 @@ export default { name: configrecord.name, value: newValue } + if (this.scopeKey === 'domainid' && !params[this.scopeKey]) { + params[this.scopeKey] = this.resource?.id + } postAPI('updateConfiguration', params).then(json => { - this.editableValue = this.getEditableValue(json.updateconfigurationresponse.configuration) + // The updateConfiguration response can return a stale value for + // non-global scopes (server-side issue). Trust the value the user + // just submitted — the API returned success, so we know it was persisted. + const apiRecord = json.updateconfigurationresponse.configuration + configRecordEntry = { ...apiRecord, value: String(newValue) } + this.editableValue = this.getEditableValue(configRecordEntry) this.actualValue = this.editableValue this.$emit('change-config', { value: newValue }) this.$store.dispatch('RefreshFeatures') this.$messageConfigSuccess(`${this.$t('message.setting.updated')} ${configrecord.name}`, configrecord) - if (json.updateconfigurationresponse && - json.updateconfigurationresponse.configuration && - !json.updateconfigurationresponse.configuration.isdynamic && - ['Admin'].includes(this.$store.getters.userInfo.roletype)) { - this.$notification.warning({ - message: this.$t('label.status'), - description: this.$t('message.restart.mgmt.server') - }) - } + this.$notifyConfigurationValueChange(configRecordEntry) }).catch(error => { this.editableValue = this.actualValue console.error(error) @@ -305,18 +316,23 @@ export default { }) }).finally(() => { this.valueLoading = false - this.$emit('refresh') + this.$emit('refresh', configrecord.name, configRecordEntry) }) }, resetConfigurationValue (configrecord) { + let configRecordEntry = this.configrecord this.valueLoading = true this.editableValueKey = null const params = { [this.scopeKey]: this.$route.params?.id, name: configrecord.name } + if (this.scopeKey === 'domainid' && !params[this.scopeKey]) { + params[this.scopeKey] = this.resource?.id + } postAPI('resetConfiguration', params).then(json => { - this.editableValue = this.getEditableValue(json.resetconfigurationresponse.configuration) + configRecordEntry = json.resetconfigurationresponse.configuration + this.editableValue = this.getEditableValue(configRecordEntry) this.actualValue = this.editableValue var newValue = this.editableValue if (configrecord.type === 'Range') { @@ -325,15 +341,7 @@ export default { this.$emit('change-config', { value: newValue }) this.$store.dispatch('RefreshFeatures') this.$messageConfigSuccess(`${this.$t('label.setting')} ${configrecord.name} ${this.$t('label.reset.config.value')}`, configrecord) - if (json.resetconfigurationresponse && - json.resetconfigurationresponse.configuration && - !json.resetconfigurationresponse.configuration.isdynamic && - ['Admin'].includes(this.$store.getters.userInfo.roletype)) { - this.$notification.warning({ - message: this.$t('label.status'), - description: this.$t('message.restart.mgmt.server') - }) - } + this.$notifyConfigurationValueChange(json?.resetconfigurationresponse?.configuration || null) }).catch(error => { this.editableValue = this.actualValue console.error(error) @@ -344,7 +352,7 @@ export default { }) }).finally(() => { this.valueLoading = false - this.$emit('refresh') + this.$emit('refresh', configrecord.name, configRecordEntry) }) }, getEditableValue (configrecord) { diff --git a/ui/src/views/storage/ChangeSharedFSServiceOffering.vue b/ui/src/views/storage/ChangeSharedFSServiceOffering.vue index 22913881efdd..f74ad59cb019 100644 --- a/ui/src/views/storage/ChangeSharedFSServiceOffering.vue +++ b/ui/src/views/storage/ChangeSharedFSServiceOffering.vue @@ -95,7 +95,7 @@ export default { loading: false, configLoading: false, serviceofferings: [], - serviceofferingLoding: false + serviceofferingLoading: false } }, beforeCreate () { diff --git a/ui/src/views/storage/CreateSharedFS.vue b/ui/src/views/storage/CreateSharedFS.vue index 4e8c56e38ee2..0d2969140556 100644 --- a/ui/src/views/storage/CreateSharedFS.vue +++ b/ui/src/views/storage/CreateSharedFS.vue @@ -272,23 +272,21 @@ export default { }, fetchOwnerOptions (OwnerOptions) { this.owner = {} - console.log('fetching owner') if (OwnerOptions.selectedAccountType === 'Account') { if (!OwnerOptions.selectedAccount) { return } - console.log('fetched account') this.owner.account = OwnerOptions.selectedAccount this.owner.domainid = OwnerOptions.selectedDomain } else if (OwnerOptions.selectedAccountType === 'Project') { if (!OwnerOptions.selectedProject) { return } - console.log('fetched project') this.owner.projectid = OwnerOptions.selectedProject } - console.log('fetched owner') - this.fetchData() + if (OwnerOptions.initialized) { + this.fetchData() + } }, fetchData () { this.minCpu = store.getters.features.sharedfsvmmincpucount diff --git a/ui/src/views/storage/CreateTemplate.vue b/ui/src/views/storage/CreateTemplate.vue index 81f0e3e782a8..016af2662e6f 100644 --- a/ui/src/views/storage/CreateTemplate.vue +++ b/ui/src/views/storage/CreateTemplate.vue @@ -73,42 +73,32 @@ - - - - - - {{ opt.path || opt.name || opt.description }} - - - + @change-option-value="handleDomainChange" /> - - - {{ acc.name }} - - + api="listAccounts" + :apiParams="accountsApiParams" + resourceType="account" + optionValueKey="name" + optionLabelKey="name" + defaultIcon="team-outlined" + allowClear="true" + :placeholder="apiParams.account.description" /> { - this.domains = json.listdomainsresponse.domain - }).finally(() => { - this.domainLoading = false - this.handleDomainChange(null) - }) - }, - async handleDomainChange (domain) { - this.domainid = domain + handleDomainChange (domainId) { + this.domainid = domainId this.form.account = null this.account = null - if ('listAccounts' in this.$store.getters.apis) { - await this.fetchAccounts() - } - }, - fetchAccounts () { - return new Promise((resolve, reject) => { - getAPI('listAccounts', { - domainid: this.domainid - }).then(response => { - this.accounts = response?.listaccountsresponse?.account || [] - resolve(this.accounts) - }).catch(error => { - this.$notifyError(error) - }) - }) }, - handleAccountChange (acc) { - if (acc) { - this.account = acc.name + handleAccountChange (accountName) { + if (accountName) { + this.account = accountName } else { - this.account = acc + this.account = null } }, handleSubmit (e) { diff --git a/ui/src/views/storage/CreateVMFromBackup.vue b/ui/src/views/storage/CreateVMFromBackup.vue index f9dbf535d060..50fee4ea5429 100644 --- a/ui/src/views/storage/CreateVMFromBackup.vue +++ b/ui/src/views/storage/CreateVMFromBackup.vue @@ -29,12 +29,18 @@ - + + + + +